Как выполнить миллионы вычислений?

Мой код вставлен ниже. Когда я запускаю эту программу, она продолжает вычислять. Я использую старый компилятор Turbo C ++. Сколько времени должна потребоваться такая программа для выполнения? Я ждал около 5 минут, но результата не было.

/*The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.

Find the sum of all the primes below two million.
*/
#include<stdio.h>
#include<conio.h>
#define TWO_MILLION 2*1000*1000
int IsPrime(long unsigned int num);
int main()
{
    long unsigned int i,sum=0;
    clrscr();
    for(i=2;i<TWO_MILLION;i++)
    {
        if(IsPrime(i))
        sum+=i;
    }
    gotoxy(25,25);
    printf("%ld",sum);
    getch();
    return 0;
}
int IsPrime(long unsigned int num)
{
    int flag=1;
    long unsigned int i;
    for(i=2;i<num;i++)
    {
        if(num%i==0)
        {
            flag=0;
            break;
        }
    }
    return flag;
}

person Community    schedule 04.10.2010    source источник
comment
старый компилятор Turbo C ++ 1. ПОЧЕМУ О ПОЧЕМУ О ПОЧЕМУ? 2. Разве это не вопрос C ++, а не C?   -  person Billy ONeal    schedule 04.10.2010
comment
Эпический: #define TWO_MILLION 2 * 1000 * 1000   -  person karlphillip    schedule 04.10.2010
comment
Мой учебный план университета устарел.   -  person    schedule 04.10.2010
comment
Что ж, пусть он выводит что-то каждые 100, 1000 или 10000 операций. Кроме того, я подозреваю, что вам понадобится BigInt для хранения результата, а не переполнения. Вы также можете написать '.' каждый 10-й раз вы находите простое число, но не забудьте перенаправить его в файл, чтобы поддерживать приличную скорость. Затем посмотрите размер / содержание файла отдельно. Возможно, вы захотите ускорить проверку на простоту, посмотрев только на sqrt (N).   -  person Hamish Grubijan    schedule 04.10.2010
comment
если вам интересен вывод, почему бы вам просто не cout в середине цикла для визуальной обратной связи?   -  person vol7ron    schedule 04.10.2010
comment
Я сделал паузу в своей программе примерно через 5 минут и проверил часы на i, которое было слишком маленьким по сравнению со временем.   -  person    schedule 04.10.2010
comment
@fahad: это не означает, что вам нужно использовать архаичный компилятор, который не может даже создавать двоичные файлы, которые работают в любой 64-битной версии Windows. C или C ++ по-прежнему являются C или C ++, и доступны бесплатные компиляторы (msvc, MinGW), которые превосходят Turbo C и Turbo C ++ как по соответствию стандартам, так и по скорости выполнения скомпилированного кода.   -  person Billy ONeal    schedule 04.10.2010
comment
Кстати, почему вы используете int для флага вместо bool?   -  person vol7ron    schedule 04.10.2010
comment
@ vol7ron: C не имеет типа данных bool. (В C99 есть _Bool, но это уже другая история ..)   -  person Billy ONeal    schedule 04.10.2010
comment
Определите, сколько времени потребуется на выполнение 100 вычислений, тогда это даст вам среднее значение того, сколько потребуется выполнить 1 (время / 100), затем умножьте на 2 м, чтобы узнать, сколько времени это займет.   -  person James Black    schedule 04.10.2010
comment
@JamesBlack: из-за O(n^2) он более точен для времени от 1999901 до 2000000, а затем умножьте его на 0,5 * (2000000/100), чтобы получить оценку.   -  person rwong    schedule 04.10.2010
comment
ожидание .. 15 минут а петля все еще не близко к 2 миллионам лол   -  person    schedule 04.10.2010
comment
Два миллиона - это не так уж и много, но ваш алгоритм - O (n ** 2). Итак, если на создание 1 миллиона уходит 2 минуты, это займет 4 минуты gf   -  person jrockway    schedule 04.10.2010
comment
Я никогда раньше не видел, чтобы int, unsigned и long использовались в одном объявлении!   -  person Gabe    schedule 04.10.2010
comment
Turbo C++ Я предположил c++. Кто-нибудь еще программирует на c? C++ становится еще менее вероятным, поскольку C# повсюду.   -  person vol7ron    schedule 04.10.2010
comment
@rwong - Спасибо за диапазон, я не указал начальную точку, просто тот факт, что он может захотеть рассчитать время, чтобы узнать, сколько времени может потребоваться, чтобы закончить.   -  person James Black    schedule 04.10.2010
comment
@Karlphilip: Эпично? Что вы нашли в этом интересного? 20 голосов за это? Почему?   -  person    schedule 04.10.2010
comment
@ vol7ron: c до сих пор используется во многих местах. Компиляторы C УНИВЕРСАЛЬНО доступны практически на любом оборудовании (это не относится к C ++ и определенно не относится к C #). C может предоставить вам доступ к настраиваемому оборудованию. C # не может сделать это без больших усилий. C отлично подходит для многих вещей ниже пользовательского уровня.   -  person abelenky    schedule 04.10.2010
comment
@ vol7ron: 1. все еще существуют очень большие системы cough UNIX и друзья cough, которые написаны на C, а не на C ++. Возможно, в вашем узком наборе программ (тех, которые работают в Microsoft Windows) C # стал более распространенным. Но с точки зрения количества фактически работающих строк кода имейте в виду, что C # работает только на 3 процентах компьютеров или меньше. Вы не увидите этого, например, на автомобильном микроконтроллере, сотовом телефоне или Kindle. Но вы почти наверняка найдете там C.   -  person Billy ONeal    schedule 04.10.2010
comment
projecteuler.net/index.php?section=problems&id=10 есть много другие вопросы, связанные с проблемой 10, также можно найти здесь: stackoverflow.com/search?q=project+euler+10   -  person Ron Warholic    schedule 04.10.2010
comment
@Gabe: Считаете ли вы, что их использование сразу становится ненужным?   -  person    schedule 05.10.2010
comment
Фахад: Достаточно всего unsigned long.   -  person Gabe    schedule 05.10.2010
comment
@Gabe: даже для моего старого турбо-компилятора?   -  person    schedule 05.10.2010
comment
fahad: Первым компилятором, который я купил, был Turbo C ++, и я ни разу не набрал long int.   -  person Gabe    schedule 05.10.2010


Ответы (10)


Вы не делаете миллионы вычислений, вы делаете их триллионы.

IsPrime будет работать за O (n) раз, то есть будет выполнять 2 миллиона инструкций только для определения этого единственного числа. На то, чтобы потратить два миллиона времени на подобные вещи, потребуется слишком много времени.

Для этого вам действительно нужно использовать что-то вроде: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes, который может гораздо более эффективно определять все простые числа в определенном диапазоне.

person Winston Ewert    schedule 04.10.2010
comment
На самом деле два миллиона в квадрате - это всего четыре триллиона, а не квадриллион. - person Billy ONeal; 04.10.2010
comment
Да, да, Sieve - это круто, но в целом вопрос о простых числах задавался миллион раз, и есть несколько чертовски быстрых ответов. В частности, сетка ограничена тем, что вы можете легко посмотреть на первые 2 миллиона чисел, но если вам нужны первые 2 миллиона простых чисел, это становится немного сложнее. Я бы порекомендовал взглянуть на быстрый генератор простых чисел на Python. Просмотрите всю цепочку и приготовьтесь узнать что-то новое. stackoverflow.com/questions/567222/ - person Hamish Grubijan; 04.10.2010
comment
@Hamish: Я не думаю, что генератор, написанный на Python, был бы приемлемым ответом для кого-то из класса, использующего C. Более того, вопрос помечен как C. Мы не особо заботимся о ваших языковых предпочтениях здесь. - person Billy ONeal; 04.10.2010
comment
@Hamish: если у вас все в порядке с как минимум 2 миллионами простых чисел, можно вычислить верхнюю границу для n-го простого числа - так что вы можете просто найти это значение и использовать его со своим ситом . См. stackoverflow.com/questions/1042717/ для подробностей. - person Michael Madsen; 04.10.2010
comment
@Billy ON Действительно, речь идет не о языке, а о новой концепции простых чисел. Если бы спрашивающий не поленился и поискал бы другие вопросы о простых числах, он бы нашел отличные ответы на SO. Я надеюсь принести пользу будущим читателям, и я не очень хочу помочь с домашним заданием, особенно когда это проблема, которая была поставлена, вероятно, буквально миллиону студентов, изучающих информатику, за последние несколько десятилетий. - person Hamish Grubijan; 04.10.2010
comment
@Hamish: Если не о языке, то зачем вообще был выращен Python? Конечно, больше нигде в этой ветке об этом не упоминалось. Прошу прощения, если это была реакция коленного рефлекса - я просто устаю от каждого случая, когда кто-то задает вопрос, получая хороший ответ, просто напишите его в X вместо того, чтобы фактически пытаться ответить на исходный вопрос. Возможно, это не было вашим намерением, но так оно и произошло. Извините, если я неправильно понял. - person Billy ONeal; 04.10.2010

Сколько времени должно занять выполнение такой программы?

Это полностью зависит от вашей платформы. Я подозреваю, что, поскольку вы выполняете ~ (два миллиона) ^ 2 операций (~ четыре триллиона) вычислений, ужасно долго.

Есть гораздо лучшие способы выполнить то, что вы делаете - например, чтобы проверить простое число, вам нужно проверить только квадратный корень из числа, а не полностью до самого числа. Не говоря уже о том, что, вероятно, существует решение для динамического программирования, которое может сделать это намного быстрее, чем это.

person Billy ONeal    schedule 04.10.2010
comment
Самое безумное в том, что четыре триллиона операций по модулю (на самом деле гораздо меньше, потому что он, по крайней мере, реализовал ранний выход) не обязательно недопустимы на современном оборудовании. Если использовать графический процессор с тактовой частотой 1 ГГц, который выполняет 256 операций за цикл (консервативно), у вас будет 250 миллиардов операций в секунду, или 10-20 секунд, плюс-минус. - person Potatoswatter; 04.10.2010
comment
@Potatoswatter: x86 определенно не может выполнять 256 операций за цикл. Там вы выполняете одну операцию за цикл, если вам повезет (возможно, некоторые странности с SSE, но там вы получаете максимум 4-8 операций / цикл, и это также только с плавающей запятой). Возможно, вы имеете в виду какую-то архитектуру RISC или VLSI? - person Billy ONeal; 04.10.2010
comment
@Potatoswatter: А. В этом есть смысл. Предоставляет ли CUDA или эквивалент AMD целочисленные операции? Почему-то я думал, что это все с плавающей запятой одинарной точности ... - person Billy ONeal; 04.10.2010
comment
@Billy: Нет, целые числа первоклассные. Без них управление потоком и индексирование массивов было бы настоящей проблемой, хотя я уверен, что есть архитектура, которая с этим справилась. Тем не менее, медленность деления, вероятно, сделала бы мои расчеты ошибочными. (Конечно, здесь можно легко оптимизировать разделение, но дело здесь в работе с глупыми программами.) - person Potatoswatter; 04.10.2010
comment
@Potatoswatter: Хм .. звучит очень мило. Собираюсь взглянуть ....: P - person Billy ONeal; 04.10.2010
comment
Ой, я идиот. Перечитайте вышесказанное - я имел в виду VLIW, а не VLSI: P - person Billy ONeal; 04.10.2010

Как говорили другие, это займет много времени. Альтернативный и интересный подход - Сито Эратосфена. Вы можете прочитать об этом по адресу:

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

Обычно вы инициализируете массив числами 2 ... 2 миллиона. Наименьшее еще не обработанное число - простое. Затем вы удаляете из массива все числа, кратные этому числу, и продолжаете. Он будет работать намного быстрее, чем у вас есть.

person Nathan S.    schedule 04.10.2010
comment
Я подозреваю, что единственная причина, по которой этот ответ не набирает тонны голосов, заключается в том, что @Winston дал, по сути, тот же ответ, но примерно на 4 минуты раньше. - person Billy ONeal; 04.10.2010
comment
Похоже на то. Я проверил, что никто не разместил это, прежде чем я просмотрел ссылку и ответил, но я думаю, что в то время я был не единственным, у кого была такая идея! - person Nathan S.; 04.10.2010

Необычный ответ

gotoxy(25,25);

Вы запускаете свою программу в текстовом режиме? Если размер текстового экрана только 80 x 25, и если 25-я строка закрыта какими-то другими вещами, то, скорее всего, вы не увидите никаких изменений на текстовом экране.

person rwong    schedule 04.10.2010
comment
Скорее всего, к 25-й строке осуществляется доступ с помощью gotoxy(25,24), поэтому он фактически отображается за пределами экрана! - person Gabe; 04.10.2010
comment
25,25 не уводит вас куда-нибудь в середину экрана? - person ; 04.10.2010
comment
@fahad: Только если ваш терминал состоит из 50 строк. В большинстве терминалов всего 25 линий (по крайней мере, стандартные). - person Billy ONeal; 04.10.2010

Как говорили другие: проверьте пределы своей реализации

Если TurboC++ имеет ‹limits.h›, для этих ограничений реализации есть соответствующий макрос, определенный в этом заголовке.

#include <limits.h>
#include <stdio.h>
int main(void) {
    printf("int goes from %d to %d.\n", INT_MIN, INT_MAX);
    printf("long goes from %ld to %ld.\n", LONG_MIN, LONG_MAX);
    return 0;
}

Если это не удается, вам нужно «рассчитать» пределы самостоятельно. Я перехожу на unsigned, потому что у них нет проблем с переполнением, и мне нужно только «вычислить» верхний предел (нижний предел равен 0)

#include <stdio.h>
int main(void) {
    unsigned u;
    unsigned long lu;

    u = -1; lu = -1;
    printf("unsigned ints go all the way to %u\n", u);
    printf("unsigned longs go all the way to %lu\n", lu);
    return 0;
}

В моей системе первая программа выводит

int goes from -2147483648 to 2147483647.
long goes from -9223372036854775808 to 9223372036854775807.

и 2-й программный выход

unsigned ints go all the way to 4294967295
unsigned longs go all the way to 18446744073709551615
person pmg    schedule 04.10.2010
comment
Я предполагаю, что вы действительно создали этот вывод из чего-то вроде GCC, потому что TurboC и его друзья создают 16-битные приложения, которые (в первую очередь) требуют, чтобы NTVDM действительно выполнялся. (short - 16 бит, int и long - 32 бита) - person Billy ONeal; 04.10.2010
comment
Да, я построил и запустил это на 64-битном Linux. Но результаты на моей машине не являются важной частью: важны результаты на машине OP (у меня была виртуальная машина с DOS 6.2 при моей предыдущей установке. Может быть, я переустановлю и ее здесь) - person pmg; 05.10.2010
comment
Мой int намного меньше твоего - person ; 05.10.2010
comment
Я бы хотел, чтобы мои unsigned long long были 128 бит: ... вплоть до 340282366920938463463374607431768211455 (39 цифр) - person pmg; 06.10.2010

По-прежнему никаких комментариев / ответов о константе, кроме "Epic" ...

#define TWO_MILLION 2*1000*1000

Это плохо. Когда вы измените значение позже, у вас либо будет несоответствие имени и содержания:

#define TWO_MILLION 5*1000*1000

или вы переименуете его в

#define FIVE_MILLION 5*1000*1000

и вам нужно менять его везде, где вы его использовали. Не называйте свои константы после содержимого, это просто превратит ваши магические числа в волшебные имена. Назовите их по их значению, например MAX_NUMBER UPPER_LIMIT RANGE_TO_TEST или как там лучше.

person Secure    schedule 04.10.2010
comment
Константы меняются редко. - person ; 04.10.2010
comment
@fahad: Тогда зачем вообще использовать определение и не писать само число в коде? Это считается плохой практикой программирования. Приведенный выше комментарий к Epic с 20+ голосами относится к тому факту, что такой код с высокой вероятностью будет размещен на thedailywtf.com. Просмотрите статьи там, вы найдете достаточно примеров. - person Secure; 04.10.2010
comment
Я не думаю, что люди возражали бы против: #define UPPER_LIMIT 2 * 1000 * 1000 // Два миллиона. Тогда вы можете изменить UPPER_LIMIT на любое другое число без повсеместных изменений кода. - person abelenky; 05.10.2010
comment
Спасибо, что объяснили, как назвать константу - person ; 05.10.2010

вы также можете использовать методы сита, которые не намного сложнее того, что вы используете. Идея состоит в том, чтобы выбрать первые n последовательных простых чисел и использовать их для построения сита. Я обсуждаю это (с доказательствами) в моем ответе на другой вопрос и Шелдон Л. Купер предоставляет реализацию в своем. Я не думаю, что он получил достаточно голосов за это (у меня уже есть «хороший ответ», так что, возможно, вы могли бы ему помочь в этом.

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

person aaronasterling    schedule 04.10.2010

Это может занять очень много времени.

Добавьте это, чтобы увидеть свой прогресс (хотя это займет еще больше времени):

for(i=2;i<num;i++)
    {
        if(num%i==0)
        {
            flag=0;
            printf("%d,", num); /* <== show the prime */
            break;
        }
    }

Изменить

Как указывают другие, это самый медленный способ подсчета простых чисел. Возможно, цель вашего задания - помочь вам найти (и реализовать) более быстрые?

person egrunin    schedule 04.10.2010
comment
Однако не намного дольше - большую часть времени на поиск простых чисел не потратите. +1 - person Billy ONeal; 04.10.2010
comment
-1, не обижайся. не ответ, а просто визуализация того, что было сказано в моем комментарии более часа назад. - person vol7ron; 04.10.2010
comment
@ vol7ron: ммм, что? SO перечисляет их как 11 часов назад. И даже если вы были правы, раздавать негативы за дубликаты не имеет смысла. - person egrunin; 04.10.2010

Ваша программа приводит к целочисленному переполнению, вы можете использовать long long, чтобы исправить это.

Кроме того, ваш алгоритм проверки того, является ли число простым, не очень хорош. Другой не менее простой способ - проверить числа 2 на квадратный корень из числа. Вам нужно только проверить квадратный корень из числа, чтобы определить, является ли оно простым.

person syork    schedule 04.10.2010

Простое изменение покажет вам, насколько быстро ваша программа работает и сколько работы она должна сделать. Ваш статус легко распечатать примерно через каждые 100 итераций. (Или вы можете установить его на 1000 или 10000 итераций.)


Помните, что вы можете УДВОИТЬ скорость вашего цикла за IsPrime.
После того, как вы отметите 2, вам нужно только проверить нечетные числа, и вы можете продвигаться с i+=2 вместо i++.

Если вас беспокоит скорость, почему вы тратите столько времени на проверку четных чисел? (обратите внимание, что как только вы начнете использовать только нечетные числа, вам также нужно будет изменить выходной тест, чтобы он был нечетным числом)

Вы также можете УДВОИТЬ скорость цикла в main, также избегая там четных чисел. (опять же, вам нужно использовать частный случай 2, затем использовать i + = 2, начиная с 3, чтобы получить 3, 5, 7, 9 ....)

Если цикл в IsPrime выполняется вдвое быстрее, а цикл в main - в два раза быстрее, это должно ускорить выполнение вашей программы в 4 раза. (если бы раньше это занимало час, теперь это будет 15 минут.)


Другое большое улучшение скорости, которое вы могли бы сделать, - это запустить цикл только до sqrt(num), а не до num.

Поскольку я ненавижу использовать функции с плавающей запятой, такие как sqrt, я вместо этого предлагаю близкое приближение, которое остановится в пределах 100 итераций прохождения границы sqrt, а также регулярно показываю обновления статуса.

if (num%2 == 0)
{
    flag=0;
    return flag;
}

/* Now that we checked 2, we only need to check odd numbers from now on. */
for(i=3;i<num;i+=2)
{
    if (i%101 == 0)
    {
        printf("i is %d out of %d\n", i, num);
        if (i*i > num)
        {
            break;  /* If you pass the sqrt boundary, quit. */
        }
    }

    if(num%i==0)
    {
        flag=0;
        break;
    }
}

P.S. Я поместил этот код в проект на C # (незначительное портирование). Конечно, теперь это работало на 64-битной ОС с улучшенным компилятором и процессором с тактовой частотой 2,8 ГГц.
Он работал менее чем за 20 секунд.

person abelenky    schedule 04.10.2010
comment
почему вы предпочитаете вычислять квадрат индекса n / 101 раз вместо того, чтобы вычислять квадратный корень из n один раз? - person aaronasterling; 04.10.2010
comment
Вопрос помечен как Turbo-C. В этом компиляторе библиотека с плавающей запятой является отдельной, и ее связывание резко увеличивает размер исполняемого файла и даже ограничивает вас процессорами с FPU (да, в свое время не все процессоры имели FPU). Определенно, есть хороший аргумент в пользу одной операции sqrt, но я подозреваю, что отформатированный printf каждые 100 операторов намного больше загружает процессор, чем простое целочисленное умножение. Добавление целочисленного умножения - это больше мой стиль и очень дешево по сравнению с другими операциями в той же области. - person abelenky; 04.10.2010
comment
Этот ответ оптимизирует вещи, но проблема здесь в том, что ОП изначально использовал неправильный алгоритм, а не то, что его текущее решение является обязательно медленной реализацией этого алгоритма. - person Billy ONeal; 04.10.2010
comment
@ Билли: Я согласен, но я отвечаю на заданный вопрос. Я бы предпочел сказать: «Вы делаете это неправильно ... измените свой алгоритм с нуля, но это не очень поможет ученику». - person abelenky; 04.10.2010
comment
Чем это не поможет студенту? - person Billy ONeal; 04.10.2010