Вывести все минимальные числа

109
12 августа 2019, 02:00

Есть такое задание: в массиве найти минимальное число. Если минимальных чисел несколько, то присвоить им среднее арифметическое исходного массива и вывести измененный массив.

Собственно, я это все сделал, однако хочу оптимизировать код, чтобы было меньше строк.

Я не прошу писать код за меня, я хочу, чтобы меня подтолкнули в правильном направлении. На другом форуме говорят, что можно все реализовать через 2 цикла, но я уже 2 дня думаю и никак не могу сообразить, как сделать.

#include "pch.h"
#include <iostream>
#include <windows.h>
#include <ctime>
int main()
{
    SetConsoleOutputCP(1251);
    srand(time(0));
    double array[10];
    double minNumber = 0.0, average = 0.0;
    int counter(0);
    //Заполнение и вывод массива
    printf("Исходный массив:\n");
    for (int i = 0; i < 10; i++) {
        array[i] = 1 + rand() % 9;  //Заполнение массива случайными числами от 1 до 9
        printf("%.2f ", array[i]);      
        average = average + array[i];   //Суммируем все элементы массива
        if (i == 9)                     //Когда массив заполнится, находим среднее арифметическое
            average = average / 10;
    }
    minNumber = array[0];   
    for (int i = 0; i < 10; i++)
    {
        if (minNumber > array[i]) { //Сравнение элементов массива
            minNumber = array[i];   //Присвоим minNumber минимальное значение
            }   
    }
    printf("\nМинимальное значение: %.2f\n", minNumber);
    for (int i = 0; i < 10; i++)
    {
        if (array[i] == minNumber) {
            counter++;
            array[i] = average;
        }
    }
    if(counter >= 2)
    {
        printf("\nИзмененный массив: \n");
        for (int i = 0; i < 10; i++)
            printf("%.2f ", array[i]);
    }
    printf("\n");
    system("pause");
    return 0;
}
Answer 1

Я уже писал в комментарии к вашему вопросу, что,, обычно, чем ниже квалификация программиста, тем сильнее у него желание "оптимизировать" код, стараясь, в частности, все запихнуть в один цикл. В результате код становится трудно-читаемым, а конструкции программы сильно перегружены. Старайтесь в первую очередь писать логически ясный код

Трудно-читаемый код - это всегда источник различных багов, и к тому же такой код трудно модифицировать

У вас в задании написано:

в массиве найти минимальное число. Если минимальных чисел несколько, то присвоить им среднее арифметическое исходного массива и вывести измененный массив.

Вот и следуйте букве этого задания. То есть вы сначала должны определить минимальное значение элементов в массиве, а также сколько элементов массива содержит это минимальное значение.

И только в случае, если таких элементов больше одного, то тогда заменить значения таких элементов средним значением. То есть эта часть программы выполняется только при одном условии.

Не надо делать в данной момент программы то, что в ней не требуется делать. Иначе такой код, в котором что-то делается, что в данный момент не требуется делать, будет вызывать вопросы и недоумения.

Поэтому в первую очередь надо думать не о пресловутой "оптимизации", а о качественно написанном коде логически ясным.

Старайтесь не использовать в коде магических чисел таких, как, например, 10. Используйте именованные константы.

Вот как может выглядеть ваш код.

#include <iostream>
#include <cstdlib>
#include <ctime>
int main() 
{
    const size_t N = 10;
    double a[N];
    std::srand( ( unsigned int )std::time( nullptr ) );
    int MAX_VALUE = 9;
    for ( auto &item : a ) item = 1 + std::rand() % MAX_VALUE;
    for ( const auto &item : a ) std::cout << item << ' ';
    std::cout << '\n';
    double min   = a[0];
    size_t count = 1;
    for ( size_t i = 1; i < N; i++ )
    {
        if ( a[i] < min )
        {
            min = a[i];
            count = 1;
        }
        else if ( not( min < a[i] ) )
        {
            ++count;
        }
    }
    if ( count > 1 )
    {
        double sum = 0.0;
        for ( const auto &item : a ) sum += item;
        double average = sum / N;
        for ( auto &item : a )
        {
            if ( item == min ) item = average;
        }
    }
    for ( const auto &item : a ) std::cout << item << ' ';
    std::cout << '\n';
    return 0;
}

Вывод программы на консоль может быть, к примеру, следующим:

3 9 6 3 2 6 9 2 8 9 
3 9 6 3 5.7 6 9 5.7 8 9

То есть в данном конкретном случае два элемента массива содержат минимальное значение, равное 2. А потмоу значения этих двух элементов массива заменяется средним арифметическим значением.

Итак, программа сначала ищет минимальное значение и количество элементов, содержащих это минимальное значение

    double min   = a[0];
    size_t count = 1;
    for ( size_t i = 1; i < N; i++ )
    {
        if ( a[i] < min )
        {
            min = a[i];
            count = 1;
        }
        else if ( not( min < a[i] ) )
        {
            ++count;
        }
    }

И лишь затем, если число элементов, содержащих найденное минимальное значение, больше 1, то происходит замена значений этих элементов средним значением.

    if ( count > 1 )
    {
        double sum = 0.0;
        for ( const auto &item : a ) sum += item;
        double average = sum / N;
        for ( auto &item : a )
        {
            if ( item == min ) item = average;
        }
    }

Теперь, когда имеется логически ясный код, то его легко "оптимизировать", как вы к тому стремитесь. Можно исключить отдельный цикл, который подсчитывает сумму всех элементов массива для вычисления далее среднего арифметического. (На самом деле это будет кажущаяся оптимизация, так как теперь, даже если в массиве имеется только один элемент с минимальным значением, происходит безусловное вычисление суммы всех элементов.)

В этом случае программа будет выглядеть следующим образом:

#include <iostream>
#include <cstdlib>
#include <ctime>
int main() 
{
    const size_t N = 10;
    double a[N];
    std::srand( ( unsigned int )std::time( nullptr ) );
    int MAX_VALUE = 9;
    for ( auto &item : a ) item = 1 + std::rand() % MAX_VALUE;
    for ( const auto &item : a ) std::cout << item << ' ';
    std::cout << '\n';
    double min   = a[0];
    size_t count = 1;
    //  preliminarily calculating the sum of all elements of the array
    //  used in the if ( count > 1 ) code block to escape a redundant loop
    double sum = a[0];
    for ( size_t i = 1; i < N; i++ )
    {
        sum += a[i];
        if ( a[i] < min )
        {
            min = a[i];
            count = 1;
        }
        else if ( not( min < a[i] ) )
        {
            ++count;
        }
    }
    if ( count > 1 )
    {
        double average = sum / N;
        for ( auto &item : a )
        {
            if ( item == min ) item = average;
        }
    }
    for ( const auto &item : a ) std::cout << item << ' ';
    std::cout << '\n';
    return 0;
}

Желательно перед циклом, который вычисляет сумму элементов, поместить комментарий, объясняющий, почему мы решили сделать это имеено в этом месте программы.

Answer 2

Собственно, я это все сделал, однако хочу оптимизировать код, чтобы было меньше строк.

Разделите ваш код на функции, после чего будет видно где требуется провести оптимизацию.

 #include <iostream>
// В (double array[]) и (double * array) - эквивалентные записи.
void fillArray(double * array) 
{
    //Заполнить массив
    ...
}
void showArray(double * array)
{
    // Отобразить массив
}
double arichmeticAverage(double * array)
{
    double average = 0;
    //Найти среднее арифметическое
    ...
    return average;
}
double minNumber (double * array)
{
    double min = 0;
    //Поиск минимально значения
    ...
    return min;
}
double something (double * array)
{
    // Something
    return ...;
}
int main()
{
    double array[100];
    fillArray(array);
    showArray(array);
    double average = arichmeticAverage(array);
    double min = minNumber(array);
    ...
    ...
    return 0;
}
Answer 3

Вот вам с двумя циклами. Учитесь пользоваться контейнерами, с ними и ошибок меньше и код получается проще. Можно было бы конечно обойтись и без них, но код получился бы намного сложнее и если не писать свой список, то пришлось бы добавлять ещё один массив того же размера что и исходный (это хорошо у вас 10 элементов... а если 10 тысяч? :) )

#include <iostream>
#include <iomanip>
#include <ctime>
#include <array>
#include <list>
#include <cmath>
#include <windows.h>
int main()
{
  SetConsoleOutputCP(1251);
  const int arraySize = 10;
  srand(static_cast<unsigned>(time(nullptr)));
  std::array<double, arraySize> dataArray; // исходный массив
  double minNumber = 1e9; // инициализируем заведомо бОльшим значением
  double average = 0.0;
  double currentData; // просто для ускорения работы
  std::list<unsigned> indexes; // список индексов минимальных чисел
  std::cout << "Исходный массив:" << std::endl;
  for (unsigned i = 0; i < dataArray.size(); i++) {
    currentData = 1 + rand() % 9; // генерируем случайное число от 1 до 9
    dataArray[i] = currentData;  //Заполнение массива
    std::cout << std::setprecision(2) << currentData << ' '; // выводим массив
    if (minNumber > currentData) {
      minNumber = currentData; // нашли новый минимум
      indexes.clear(); // очищаем ранее найденные индексы минимума
      indexes.push_back(i); // запоминаем индекс нового минимума
    }
    // далее можно было бы поставить обычное равенство, но с вещественным числами лучше поступать примерно так:
    else if (fabs(minNumber-currentData)<1e-9) {
      // нашли ещё один элемент с текущим минимумом
      indexes.push_back(i);
    }
    average += currentData; // внутри цикла только накапливаем, проверка заполнения не нужна
  }
  // среднее значение получаем после цикла
  average /= dataArray.size();
  std::cout << std::endl << "Минимальное значение: "
            << std::setprecision(2) << minNumber << std::endl;
  // теперь меняем массив и сразу печатаем
  std::cout << "Измененный массив:" << std::endl;
  for (unsigned i = 0; i < dataArray.size(); i++) {
    if (i == indexes.front()) { // текущий элемент массива - минимум?
      dataArray[i] = average; // меняем его
      indexes.pop_front(); // удаляем изменённый элемент из списка индексов
    }
    std::cout << std::setprecision(2) << dataArray[i] << ' '; // выводим массив
  }
  std::cout << std::endl;
  system("pause");
  return 0;
}
Answer 4

Ну, например, вот через два цикла(как вы хотели)

double array[10];
size_t  quantity = 0;
// тут инициализируем array[0]
double minNumber = array[0], average = array[0];
for (int i = 1; i < 10; ++i) {
    // инициализируем array[i]
    average += array[i];
    if (minNumber > array[i])
        minNumber = array[i];
}
for (int i = 0; i < 20; ++i) {
    if (i >= 10 ) {
        if (quantity < 2)
            break;
        printf("%.2f ", array[i % 10]);
    }
    else if ( array[i] == minNumber) {
        ++quantity;
        array[i] = average / 10;
    }
}

p.s. если ваш массив будет содержать целые, то вам нужен массив для типов int, а не double

READ ALSO
Создать список функций

Создать список функций

И если есть советы как запомнить, буду благодарен)

121
Объект класса в качестве значения map

Объект класса в качестве значения map

Пытаюсь добавить в map элемент класса, выдает что нет подходящего конструктораВот пример кода (для примера взял структуру, но так же не работает...

131
Ошибка компиляции на месте вызова printf()

Ошибка компиляции на месте вызова printf()

Почему вот это работает,

122
Как расширять структуру / класс вне его / ее объявления?

Как расширять структуру / класс вне его / ее объявления?

Никак не могу понять как на примере интерпретатора python можно расширять главный PyObject дополняя его методами и другими объектами не правя саму...

99