Nano Hash - криптовалюты, майнинг, программирование

Неожиданные результаты при работе с очень большими целыми числами на интерпретируемых языках

Я пытаюсь получить сумму 1 + 2 + ... + 1000000000, но получаю забавные результаты в PHP и Node.js.

PHP

$sum = 0;
for($i = 0; $i <= 1000000000 ; $i++) {
    $sum += $i;
}
printf("%s", number_format($sum, 0, "", ""));   // 500000000067108992

Node.js

var sum = 0;
for (i = 0; i <= 1000000000; i++) {
    sum += i ;
}
console.log(sum); // 500000000067109000

Правильный ответ можно рассчитать с помощью

1 + 2 + ... + n = n(n+1)/2

Правильный ответ = 500000000500000000, поэтому я решил попробовать другой язык.

GO

var sum , i int64
for i = 0 ; i <= 1000000000; i++ {
    sum += i
}
fmt.Println(sum) // 500000000500000000

Но работает нормально! Так что не так с моим кодом PHP и Node.js?

Возможно, это проблема интерпретируемых языков, и поэтому она работает на компилируемом языке вроде Go? Если да, будут ли другие интерпретируемые языки, такие как Python и Perl, иметь такую ​​же проблему?


  • вам нужно это: php.net/manual/en/book.bc.php, а то вы будете ломать голову о IEEE 754, пока ад не замерзнет. 04.08.2013
  • bcadd почти навсегда .. Мне пришлось сдаться 04.08.2013
  • То же самое дает не только NodeJS, но и клиентский JavaScript. 500000000067109000 04.08.2013
  • Для обработки больших чисел в PHP (т. Е. 64-битных) используйте функции GMP, в данном случае gmp_add (). 05.08.2013
  • Я играл с этим, и я немного озадачен тем, что происходит под капотом на этих двух языках, чтобы получить 500000000067108992. Любопытно, что я могу получить точно 499999999067108992 на C ++, переключившись с int на double непосредственно перед переполнением, когда суммируя ряды в цикле, я также не понимаю, почему я теряю точность в этом случае. 05.08.2013
  • Для сверхэффективности ваши циклы действительно должны начинаться с 1, а не с 0.: P 05.08.2013
  • сумма (от 1 до N) = (N / 2) * (N + 1) 05.08.2013
  • хм ... Я получаю 499999999500000000 в java (long). Как-то странно 05.08.2013
  • связанные: stackoverflow.com/q/4427020/684934 05.08.2013
  • @GrahamBorland, как начать с 1 повысить эффективность? 05.08.2013
  • @Baba 0 является лишним для ваших вычислений, поэтому нет необходимости в дополнительной итерации цикла для прибавления 0 к 0. 05.08.2013
  • @Baba вы запускаете цикл for только 10 ^ 9 раз вместо (1 + 10 ^ 9) раз :-p 05.08.2013
  • Вы можете использовать int-hinting для использования целочисленных операций вместо операций с плавающей запятой, к сожалению, результат не подходит для 32-битных целых чисел. Тем не менее, стоит поделиться: gist.github.com/jcdickinson/6155436 05.08.2013
  • @ Maver1ck: в java используйте большое целое число (docs .oracle.com / javase / 1.4.2 / docs / api / java / math / BigInteger.html) для такого рода вычислений :) 05.08.2013
  • Отличный вопрос. Кстати, вы можете сделать свой код Go еще быстрее, используя параллелизм! 05.08.2013
  • @ Maver1ck Вы, наверное, просто не сделали инклюзивный цикл. 05.08.2013
  • Хм ... Я получаю -5340232216128654848 из кода Go (go1.1.1) 05.08.2013
  • Как такое возможно? вы используете 32 или 63 бита? 05.08.2013
  • Точно так же, как указано выше, поэтому 64-битная и 64-битная система 05.08.2013
  • может кто-нибудь объяснить, почему go дает -5340232216128654848 в некоторых определенных системах? 05.08.2013
  • Ах, в приведенном выше коде есть лишний 0 в длинном числе. 05.08.2013
  • Почему это вики сообщества? 06.08.2013
  • @jsn Это происходит автоматически, если на вопрос более 30 ответов. (См. версии этого вопроса.) 06.08.2013
  • Как говорит @flornquake, вопрос был внесен в вики сообщества, потому что на него есть 30 ответов. Судя по всему, что я вижу из вопросов и ответов, этот пост в значительной степени является иллюстрацией того, почему был добавлен именно этот автоматический триггер для Community Wiki. 06.08.2013
  • @Baba для таких случаев используйте математику вместо циклов .. math aint hard brah 06.08.2013
  • PHP 5.3.10-1ubuntu3.7 с Suhosin-Patch (cli) на 64-битной Ubuntu 12.04 Precise и вышел с: 500000000500000000 06.08.2013
  • Почему люди, публикующие один и тот же образец кода, конвертируются на свой любимый язык? Разве они не должны отвечать на этот вопрос, а не публиковать больше бесполезных фрагментов? 07.08.2013
  • @ R.id.pandacoder Не из эгоизма, но вы видели мой ответ. 20.08.2013

Ответы:


1

Python работает:

>>> sum(x for x in xrange(1000000000 + 1))
500000000500000000

Or:

>>> sum(xrange(1000000000+1))
500000000500000000

Функция Python int auto превращается в Python long, который поддерживает произвольную точность. Он даст правильный ответ на 32- или 64-битных платформах.

Это можно увидеть, возведя 2 в степень, намного превышающую разрядность платформы:

>>> 2**99
633825300114114700748351602688L

Вы можете продемонстрировать (с помощью Python), что ошибочные значения, которые вы получаете в PHP, вызваны тем, что PHP переходит в число с плавающей запятой, когда значения больше 2 ** 32-1:

>>> int(sum(float(x) for x in xrange(1000000000+1)))
500000000067108992
04.08.2013
  • Вы запускали это в 32- или 64-битной системе? 04.08.2013
  • 64-битная сборка на OS X. Занимает около 25 секунд. 04.08.2013
  • Спасибо за информацию ... Посмотрим, сможет ли кто-нибудь запустить это на 32-битном и посмотреть, будет ли результат такой же .. также протестируйте на 64-битных окнах .. он работает +1 04.08.2013
  • Он должен работать независимо (32 против 64 бит), поскольку целые числа Python автоматически продвигаются к произвольной точности, а не к переполнению. Это может занять немного больше времени. 04.08.2013
  • Python в любой системе будет работать в этом случае, поскольку Python автоматически переключается на длинные целые числа, если это необходимо. А если этого недостаточно, он также переключится на большие целые числа. 04.08.2013
  • @ 0x499602D2: Это довольно жестко. За это проголосовал сам ОП. Он конкретно спросил, похожа ли эта проблема на Python. Ответьте, нет. Код, чтобы показать, что это не так. WTH? 04.08.2013
  • Пример Python слишком длинный, просто используйте sum (xrange (int (1e9) +1)) (.... sum работает с итерациями) 05.08.2013

  • 2

    В вашем коде Go используется целочисленная арифметика с достаточным количеством битов, чтобы дать точный ответ. Никогда не касался PHP или Node.js, но, судя по результатам, я подозреваю, что вычисления производятся с использованием числа с плавающей запятой, и поэтому следует ожидать, что он не будет точным для чисел такой величины.

    04.08.2013
  • Ага. If PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead. Also, an operation which results in a number beyond the bounds of the integer type will return a float instead. - php.net/manual/en/language.types.integer.php < / а> 04.08.2013
  • А в NodeJS (и в JavaScript в целом) все арифметические операции (кроме битовых операций) ведут себя так, как если бы они были выполнены с числами с плавающей запятой. Независимо от того, есть они на самом деле или нет, это внутреннее различие, которое зависит от решений отдельных движков JavaScript. 05.08.2013
  • В спецификации javascript нет целочисленных типов. Все числа с плавающей точкой. 05.08.2013
  • @grasGendarme Есть. Спецификация ES5 определяет различные целочисленные преобразования и требует, чтобы они вызывались , например, в побитовых сдвигах. То есть за кулисами, целые типы используются в Javascript, но все арифметические операторы преобразуют свои операнды в числа с плавающей запятой, прежде чем что-либо делать с ними (за исключением оптимизации компилятора). 05.08.2013
  • И SpiderMonkey (Firefox), и V8 (Chrome) также предоставляют расширение Math.imul, потому что даже ((x|0)*(y|0))|0) недостаточно для обеспечения целочисленной семантики. Тот факт, что используются целые числа, довольно сильно просачивается. 05.08.2013
  • bitrayne утверждает, что -5340232216128654848 включен 64 бита go1.1.1 ... Есть идеи? 05.08.2013
  • @Baba: Хороший улов, в версии Go for i = 0 ; i <= 10000000000; i++ { есть один ноль ко многим, чтобы получить правильное число fmt.Println(sum) // 500000000500000000. Во всех других языковых примерах используется правильный 1000000000. 05.08.2013
  • Разве это не должно зависеть от длины слова машины и т. Д.? Зачем конкретному языку делать это в своем собственном дизайне? 06.08.2013
  • Удивительно, но мне удалось получить 32-битную систему, и я получил 500000000067108992 ?? то же, что и PHP 12.08.2013
  • @Baba: Я не могу найти упоминания о «Was». Не могли бы вы предоставить какой-нибудь указатель или ссылку? 12.08.2013
  • Я могу это сделать в своей системе .. Я могу дать вам шорты для экрана, если хотите 12.08.2013
  • @Baba: Пожалуйста, используйте, например, play.golang.org, чтобы поделиться кодом. Неважно, что он может там не работать. 12.08.2013
  • @jnml play составляет 64 бита .. у него нет этой проблемы .... попробуйте испортить мой код на 32-битные окна и посмотрите, что я имею в виду 12.08.2013
  • @Baba: Используйте play только как вставку кода (в данном случае). Ad попробуйте запустить мой код: вставьте где-нибудь (например, на play.golang.org) полный компилируемый код. Неважно, запустится он на play.golang.org или нет. Конечно, я попытаюсь запустить его локально (если я не вижу ошибки без запуска кода). 12.08.2013
  • вот код, я думаю, он испорчен, потому что я использовал float64, а не int64 .. Только что подтвердил это не имеет ничего общего с 32 или 64 битами 12.08.2013

  • 3

    Причина в том, что значение вашей целочисленной переменной sum превышает максимальное значение. И sum, который вы получаете, является результатом арифметики с плавающей запятой, которая включает округление. Поскольку в других ответах не упоминались точные ограничения, я решил опубликовать его.

    Максимальное целочисленное значение PHP для:

    • 32-разрядная версия: 2147483647.
    • 64-разрядная версия: 9223372036854775807.

    Это означает, что вы используете 32-битный ЦП, 32-битную ОС или 32-битную скомпилированную версию PHP. Его можно найти с помощью PHP_INT_MAX. sum будет рассчитан правильно, если вы сделаете это на 64-битной машине.

    Максимальное целочисленное значение в JavaScript - 9007199254740992. Наибольшее точное интегральное значение, с которым вы можете работать, - 2 53 (взято из этого вопрос). sum превышает этот предел.

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

    05.08.2013

    4

    Вот ответ на C для полноты:

    #include <stdio.h>
    
    int main(void)
    {
        unsigned long long sum = 0, i;
    
        for (i = 0; i <= 1000000000; i++)    //one billion
            sum += i;
    
        printf("%llu\n", sum);  //500000000500000000
    
        return 0;
    }
    

    Ключевым моментом в этом случае является использование типа данных C99 long long. Он обеспечивает самое большое примитивное хранилище, которым может управлять C, и работает очень, действительно быстро. Тип long long также будет работать практически на любой 32- или 64-битной машине.

    Есть одно предостережение: компиляторы, предоставленные Microsoft, явно не поддерживают 14-летний стандарт C99, поэтому заставить его работать в Visual Studio - полная чушь.

    05.08.2013
  • MSVC ++ - компилятор C ++, а C ++ получил long long в стандарте C ++ 11. Однако уже несколько лет это расширение для MSVC ++ и g ++. 05.08.2013
  • @MSalters Какая версия Visual Studio получила это? Я думаю, что последняя версия, которую я использовал, была последней, выпущенной 3 или 4 года назад. 05.08.2013
  • @MSalters Так что, будучи функцией C ++, она никому не поможет при компиляции простой программы на C. Я никогда не пробовал переключаться с C на C ++, поэтому не знаю, сработает ли этот обходной путь. 05.08.2013
  • И хорошо, что GCC или Clang с оптимизацией превращают весь цикл в movabsq $500000000500000000, %rsi 05.08.2013
  • @TorKlingberg Какие оптимизации? 05.08.2013
  • @CyberSkull: Думаю, VS2010. Хотя, возможно, это был VS2008. В конце концов, у них уже был __int64, так что это было просто переименование. 05.08.2013
  • Просто gcc -O3 или clang -O3. Я не знаю названия конкретной оптимизации. В основном компилятор замечает, что результат цикла не зависит от какого-либо аргумента, и вычисляет его во время компиляции. 05.08.2013
  • На любом уровне оптимизации -O through -O3 это будет movabs $0x6f05b59f17f6500,%rsi 05.08.2013
  • C99 long long имеет минимальный размер 64 бита и, насколько мне известно, является 64-битным как на 32-битных, так и на 64-битных платформах. Я не видел общей поддержки quad или octo int. 05.08.2013
  • ВК поддерживает давно, очень давно - по крайней мере 2003 согласно MSDN, а синтаксис ll printf был поддерживается в 2005 году. Кроме того, обратите внимание, что unsigned long long должен быть напечатан с %llu. 05.08.2013
  • Компиляторы, предоставляемые Microsoft, не поддерживают стандартный C99 14-летней давности, потому что C слишком мало используется в приложениях Windows. C ++ вместо. 06.08.2013
  • 1000000000 не рассматривается как int? вы не должны писать 1000000000LL или 1000000000L? 07.08.2013
  • @Gelldur Я проверил файл сборки, в этом случае (на Intel 64) это не имеет значения, поскольку один миллиард помещается в int так же хорошо, как и в long. 07.08.2013

  • 5

    Я предполагаю, что когда сумма превышает емкость собственного int (2 31 -1 = 2 147 483 647), Node.js и PHP переключаются на представление с плавающей запятой, и вы начинаете получать ошибки округления. Такой язык, как Go, вероятно, будет стараться придерживаться целочисленной формы (например, 64-битных целых чисел) как можно дольше (если, действительно, это не началось с этого). Поскольку ответ соответствует 64-битному целому числу, вычисление является точным.

    04.08.2013
  • Node.js явно не имеет типа int. Он работает в типе с плавающей запятой. 07.08.2013
  • @greyfade - Да, я думаю, это верно для всех сред, совместимых с EcmaScript. 07.08.2013
  • Разве это не (2 ** 31 - 1)? 20.09.2019
  • @ZacharyHunter - Да, это так. Спасибо, что уловили эту ошибку. 20.09.2019

  • 6

    Сценарий Perl дает нам ожидаемый результат:

    use warnings;
    use strict;
    
    my $sum = 0;
    for(my $i = 0; $i <= 1_000_000_000; $i++) {
        $sum += $i;
    }
    print $sum, "\n";  #<-- prints: 500000000500000000
    
    04.08.2013
  • Вы запускали это в 32- или 64-битной системе? 04.08.2013
  • он был выполнен на 64-битной системе 05.08.2013
  • 4.99999999067109e+017 на Perl v5.16.1 MSWin32-x86. 05.08.2013
  • Если вам действительно нужны большие числа, используйте bignum или bigint. Оба являются базовыми модулями, то есть они устанавливаются с Perl v5.8.0 или выше. См. http://perldoc.perl.org/bignum.html и http://perldoc.perl.org/bigint.html 05.08.2013
  • Я получил 500000000500000000, запустив это на 32-битном PPC Mac, работающем с Perl 5.12.4. 05.08.2013

  • 7

    Ответ на это «удивительно» прост:

    Во-первых, как большинство из вас может знать, 32-битное целое число находится в диапазоне от -2 147 483 648 до 2 147 483 647. Итак, что произойдет, если PHP получит результат БОЛЬШЕ, чем этот?

    Обычно можно ожидать немедленного "переполнения", в результате которого 2 147 483 647 + 1 превратится в −2 147 483 648. Однако это не так. ЕСЛИ PHP встречает большее число, он возвращает FLOAT вместо INT.

    Если PHP встречает число за пределами целочисленного типа, оно будет интерпретировано как число с плавающей запятой. Кроме того, операция, результатом которой является число, выходящее за пределы целочисленного типа, вместо этого вернет число с плавающей запятой.

    http://php.net/manual/en/language.types.integer.php

    При этом, и зная, что реализация PHP FLOAT соответствует формату двойной точности IEEE 754, означает, что PHP может работать с числами до 52 бит без потери точности. (В 32-битной системе)

    Итак, в точке, где ваша сумма достигает 9,007,199,254,740,992 (что составляет 2 ^ 53), значение Float, возвращаемое функцией PHP Maths, больше не будет достаточно точным.

    E:\PHP>php -r "$x=bindec(\"100000000000000000000000000000000000000000000000000000\"); echo number_format($x,0);"
    

    9,007,199,254,740,992

    E:\PHP>php -r "$x=bindec(\"100000000000000000000000000000000000000000000000000001\"); echo number_format($x,0);"
    

    9,007,199,254,740,992

    E:\PHP>php -r "$x=bindec(\"100000000000000000000000000000000000000000000000000010\"); echo number_format($x,0);"
    

    9,007,199,254,740,994

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

    С СЕЙЧАС вся математика пойдет не так при работе с типами данных по умолчанию.

    • Это та же проблема для других интерпретируемых языков, таких как Python или Perl?

    Я так не думаю. Я думаю, что это проблема языков, в которых нет типобезопасности. Хотя целочисленное переполнение, как упомянуто выше, БУДЕТ происходить на всех языках, использующих фиксированные типы данных, языки без безопасности типов могут попытаться отловить это с другими типами данных. Однако, как только они достигают своей «естественной» (заданной Системой) границы, они могут вернуть что угодно, но только не правильный результат.

    Однако на каждом языке могут быть разные потоки для такого сценария.

    06.08.2013

    8

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

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

    Другое решение - использовать алгоритм суммирования, который знает о проблеме точности и работает над ее решением. Ниже вы найдете такое же суммирование, сначала с 64-битным целым числом, затем с 64-битным числом с плавающей запятой, а затем снова с использованием плавающей запятой, но с использованием Алгоритм суммирования Кахана.

    Написано на C #, но то же самое относится и к другим языкам.

    long sum1 = 0;
    for (int i = 0; i <= 1000000000; i++)
    {
        sum1 += i ;
    }
    Console.WriteLine(sum1.ToString("N0"));
    // 500.000.000.500.000.000
    
    double sum2 = 0;
    for (int i = 0; i <= 1000000000; i++)
    {
        sum2 += i ;
    }
    Console.WriteLine(sum2.ToString("N0"));
    // 500.000.000.067.109.000
    
    double sum3 = 0;
    double error = 0;
    for (int i = 0; i <= 1000000000; i++)
    {
        double corrected = i - error;
        double temp = sum3 + corrected;
        error = (temp - sum3) - corrected;
        sum3 = temp;
    }
    Console.WriteLine(sum3.ToString("N0"));
    //500.000.000.500.000.000
    

    Суммирование Кахана дает прекрасный результат. Конечно, вычисления требуют намного больше времени. Хотите ли вы его использовать, зависит: а) от ваших требований к производительности и точности и б) от того, как ваш язык обрабатывает целочисленные типы данных и типы данных с плавающей запятой.

    05.08.2013
  • @Baba Это то же самое, что и с Node.js / JavaScript в OP. Относительно того, почему 500000000067109000 против 500000000067108992… понятия не имею. 05.08.2013
  • Возможно, Баба заинтригован использованием точек для разделителей тысяч: в английском обычно используются запятые. Вы также можете использовать нижнее подчеркивание как более нейтральное среднее значение. 06.08.2013

  • 9

    Если у вас 32-битный PHP, вы можете рассчитать его с помощью bc:

    <?php
    
    $value = 1000000000;
    echo bcdiv( bcmul( $value, $value + 1 ), 2 );
    //500000000500000000
    

    В Javascript вы должны использовать произвольную библиотеку чисел, например BigInteger:

    var value = new BigInteger(1000000000);
    console.log( value.multiply(value.add(1)).divide(2).toString());
    //500000000500000000
    

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

    04.08.2013

    10

    В Ruby:

    sum = 0
    1.upto(1000000000).each{|i|
      sum += i
    }
    puts sum
    

    Prints 500000000500000000, but takes a good 4 minutes on my 2.6 GHz Intel i7.


    У Магнуса и Джонти есть гораздо более Ruby-решение:

    1.upto(1000000000).inject(:+)
    

    To run a benchmark:

    $ time ruby -e "puts 1.upto(1000000000).inject(:+)"
    ruby -e "1.upto(1000000000).inject(:+)"  128.75s user 0.07s system 99% cpu 2:08.84 total
    
    05.08.2013
  • 1. upto (1000000000) .inject (: +) 05.08.2013
  • @Magnuss: Поначалу я думал, что попробовал именно это, но это вызвало огромную утечку памяти. Ваш вроде работает ... 05.08.2013

  • 11

    Я использую node-bigint для больших целочисленных вещей:
    https://github.com/substack/node-bigint

    var bigint = require('bigint');
    var sum = bigint(0);
    for(var i = 0; i <= 1000000000; i++) { 
      sum = sum.add(i); 
    }
    console.log(sum);
    

    Это не так быстро, как что-то, что может использовать собственный 64-битный материал для этого точного теста, но если вы попадете в большие числа, чем 64-битный, он использует libgmp под капотом, который является одной из самых быстрых библиотек произвольной точности.

    04.08.2013

    12

    занял целую вечность в рубине, но дает правильный ответ:

    (1..1000000000).reduce(:+)
     => 500000000500000000 
    
    05.08.2013

    13

    Чтобы получить правильный результат в php, я думаю, вам нужно использовать математические операторы BC: http://php.net/manual/en/ref.bc.php

    Вот правильный ответ на Scala. Вы должны использовать Long, иначе вы переполните число:

    println((1L to 1000000000L).reduce(_ + _)) // prints 500000000500000000
    
    05.08.2013

    14

    На самом деле есть крутой трюк с этой проблемой.

    Предположим, вместо этого было 1-100.

    1 + 2 + 3 + 4 + ... + 50 +

    100 + 99 + 98 + 97 + ... + 51

    = (101 + 101 + 101 + 101 + ... + 101) = 101*50

    Формула:

    Для N = 100: Выход = N / 2 * (N + 1)

    Для N = 1e9: Выход = N / 2 * (N + 1)

    Это намного быстрее, чем перебирать все эти данные в цикле. Ваш процессор поблагодарит вас за это. И вот по поводу этой самой проблемы есть интересная история:

    http://www.jimloy.com/algebra/gauss.htm

    04.08.2013
  • Как вы думаете, возможно ли пройти через все мосты через Прегель в Калининграде, не пересекая ни один мост дважды? Многие пытались и потерпели неудачу, но никто еще не доказал, что это невозможно. Это похоже на задачу, для решения которой вы будете обладать уникальной квалификацией. 05.08.2013

  • 15

    Это дает правильный результат в PHP за счет принудительного преобразования целого числа.

    $sum = (int) $sum + $i;
    
    05.08.2013

    16

    Common Lisp - один из самых быстро интерпретируемых языков * и по умолчанию правильно обрабатывает произвольно большие целые числа. Это занимает около 3 секунд с SBCL:

    * (time (let ((sum 0)) (loop :for x :from 1 :to 1000000000 :do (incf sum x)) sum))
    
    Evaluation took:
      3.068 seconds of real time
      3.064000 seconds of total run time (3.044000 user, 0.020000 system)
      99.87% CPU
      8,572,036,182 processor cycles
      0 bytes consed
    
    500000000500000000
    
    • Под интерпретацией я имею в виду, что я запускал этот код из REPL, SBCL, возможно, выполнил некоторую внутреннюю JIT-обработку, чтобы заставить его работать быстро, но динамический опыт немедленного запуска кода такой же.
    05.08.2013
  • Можно упростить как (время (цикл для x от 1 до 1000000000 сумма x)). Я получил ~ 5-кратную скорость, добавив объявление: (время (локально (declare (optimize (speed 3) (безопасность 0))) (цикл для i типа fixnum от 1 до 1000000000 сумма i типа fixnum))) 06.08.2013
  • Это ошибочно. Не позволяйте другим языкам ослеплять вас! Правильный способ записать его на лиспе: (defun sum-from-1-to-n (n) (/ (* n (1+ n)) 2)) (time (sum-from-1-to-n) 1000000000)) потребовалось 14 микросекунд (0,000014 секунды). В течение этого периода и с 4 доступными ядрами ЦП 0 микросекунд (0,000000 секунд) было потрачено в пользовательском режиме 0 микросекунд (0,000000 секунд) было потрачено в системном режиме - ›500000000500000000 06.08.2013
  • @informatimago: Это не ошибочно. Я копировал стиль императивного цикла вопроса, и, как многие отмечали, в самом вопросе упоминается, что есть более простой способ вычисления. Chillax. 06.08.2013

  • 17

    У меня недостаточно репутации, чтобы прокомментировать ответ Common Lisp @ postfuturist, но его можно оптимизировать для завершения за ~ 500 мс с SBCL 1.1.8 на моей машине:

    CL-USER> (compile nil '(lambda () 
                            (declare (optimize (speed 3) (space 0) (safety 0) (debug 0) (compilation-speed 0))) 
                            (let ((sum 0))
                              (declare (type fixnum sum))
                              (loop for i from 1 to 1000000000 do (incf sum i))
                              sum)))
    #<FUNCTION (LAMBDA ()) {1004B93CCB}>
    NIL
    NIL
    CL-USER> (time (funcall *))
    Evaluation took:
      0.531 seconds of real time
      0.531250 seconds of total run time (0.531250 user, 0.000000 system)
      100.00% CPU
      1,912,655,483 processor cycles
      0 bytes consed
    
    500000000500000000
    
    05.08.2013

    18

    Racket v 5.3.4 (MBP; время в мс):

    > (time (for/sum ([x (in-range 1000000001)]) x))
    cpu time: 2943 real time: 2954 gc time: 0
    500000000500000000
    
    05.08.2013
  • Удалил мой ответ, опубликованный через 6 минут после вас, как только я заметил ваш. :) 05.08.2013

  • 19

    Прекрасно работает в Rebol:

    >> sum: 0
    == 0
    
    >> repeat i 1000000000 [sum: sum + i]
    == 500000000500000000
    
    >> type? sum
    == integer!
    

    Здесь использовался Rebol 3, который, несмотря на 32-битную компиляцию, использует 64-битные целые числа (в отличие от Rebol 2, который использовал 32-битные целые числа)

    05.08.2013

    20

    Я хотел посмотреть, что произошло в CF Script

    <cfscript>
    ttl = 0;
    
    for (i=0;i LTE 1000000000 ;i=i+1) {
        ttl += i;
    }
    writeDump(ttl);
    abort;
    </cfscript>
    

    Получил 5.00000000067E + 017

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

    05.08.2013

    21

    ActivePerl v5.10.1 на 32-битной Windows, Intel core2duo 2.6:

    $sum = 0;
    for ($i = 0; $i <= 1000000000 ; $i++) {
      $sum += $i;
    }
    print $sum."\n";
    

    результат: 5.00000000067109e + 017 через 5 минут.

    С "use bigint" скрипт проработал два часа, а мог бы проработать и больше, но я остановил его. Слишком медленно.

    05.08.2013
  • Может ли кто-нибудь подтвердить, что именно столько времени занимает добавление такого количества бигинтов? 06.08.2013

  • 22

    Для полноты картины в Clojure (красиво, но не очень эффективно):

    (reduce + (take 1000000000 (iterate inc 1))) ; => 500000000500000000
    
    05.08.2013
  • Единственная полезная информация, которую имеют ответы $ MY_FAVOURITE_LANGUAGE, - это то, предоставляют ли они результат ... 06.08.2013
  • @jwg да, извините, я пропустил конец строки - обновленный ответ. 06.08.2013

  • 23

    AWK:

    BEGIN { s = 0; for (i = 1; i <= 1000000000; i++) s += i; print s }
    

    дает тот же неправильный результат, что и PHP:

    500000000067108992
    

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

    Тестовые прогоны:

    $ awk 'BEGIN { s = 0; for (i = 1; i <= 100000000; i++) s += i; print s }'
    5000000050000000
    $ awk 'BEGIN { s = 0; for (i = 1; i <= 1000000000; i++) s += i; print s }'
    500000000067108992
    
    07.08.2013

    24

    Категория другой переводимый язык:

    Tcl:

    Если вы используете Tcl 8.4 или более раннюю версию, это зависит от того, был ли он скомпилирован с 32-битной или 64-битной версией. (8.4 - конец жизни).

    Если вы используете Tcl 8.5 или новее с произвольными большими целыми числами, он будет отображать правильный результат.

    proc test limit {
        for {set i 0} {$i < $limit} {incr i} {
            incr result $i
        }
        return $result
    }
    test 1000000000 
    

    Я помещаю тест в процесс, чтобы он был скомпилирован в байтах.

    04.08.2013

    25

    Для кода PHP ответ находится здесь:

    Размер целого числа зависит от платформы, хотя максимальное значение около двух миллиардов является обычным значением (это 32 бита со знаком). 64-битные платформы обычно имеют максимальное значение около 9E18. PHP не поддерживает целые числа без знака. Целочисленный размер можно определить с помощью константы PHP_INT_SIZE, а максимальное значение - с помощью константы PHP_INT_MAX, начиная с PHP 4.4.0 и PHP 5.0.5.

    05.08.2013

    26

    Гавань:

    proc Main()
    
       local sum := 0, i
    
       for i := 0 to 1000000000
          sum += i
       next
    
       ? sum
    
       return
    

    Результатов в 500000000500000000. (как на windows / mingw / x86, так и на osx / clang / x64)

    05.08.2013

    27

    Erlang работает:

    from_sum(From,Max) ->
        from_sum(From,Max,Max).
    from_sum(From,Max,Sum) when From =:= Max ->
        Sum;
    from_sum(From,Max,Sum) when From =/= Max -> 
        from_sum(From+1,Max,Sum+From).
    

    Результатов: 41> бесполезно: from_sum (1,1000000000). 500000000500000000

    05.08.2013

    28

    Забавно, PHP 5.5.1 дает 499999999500000000 (за ~ 30 секунд), а Dart2Js дает 500000000067109000 (чего и следовало ожидать, поскольку выполняется именно JS). CLI Dart дает правильный ответ ... мгновенно.

    05.08.2013

    29

    Erlang тоже дает ожидаемый результат.

    sum.erl:

    -module(sum).
    -export([iter_sum/2]).
    
    iter_sum(Begin, End) -> iter_sum(Begin,End,0).
    iter_sum(Current, End, Sum) when Current > End -> Sum;
    iter_sum(Current, End, Sum) -> iter_sum(Current+1,End,Sum+Current).
    

    И используя это:

    1> c(sum).
    {ok,sum}
    2> sum:iter_sum(1,1000000000).
    500000000500000000
    
    05.08.2013

    30

    Болтовня:

    (1 to: 1000000000) inject: 0 into: [:subTotal :next | subTotal + next ]. 
    
    "500000000500000000"
    
    05.08.2013

    31

    Только для полноты.


    В MATLAB нет проблем с автоматическим выбором типа:

    tic; ii = 1:1000000; sum(ii); toc; ans

    Elapsed time is 0.004471 seconds.
    ans = 5.000005000000000e+11


    А в интерактивном F # автоматические типы единиц выдают ошибку переполнения. Присвоение типа int64 дает правильный ответ:

    seq {int64 1.. int64 1000000} |> Seq.sum

    val it : int64 = 500000500000L

    Примечания.
    Можно использовать Seq.reduce (+) вместо Seq.sum без заметного изменения эффективности. Однако использование Seq.reduce (+) с автоматическим типом единицы измерения даст неправильный ответ, а не ошибку переполнения.
    Время вычисления составляет <0,5 секунды, но в настоящее время я ленив, поэтому я не импортирую класс секундомера .NET, чтобы получить больше точное время.

    06.08.2013

    32

    Несколько ответов уже объяснили, почему ваш код PHP и Node.js не работает должным образом, поэтому я не буду повторять это здесь. Я просто хочу указать, что это не имеет ничего отношения к «интерпретируемым и компилируемым языкам».

    Возможно, это проблема интерпретируемых языков, и поэтому она работает на компилируемом языке вроде Go?

    «Язык» - это просто набор четко определенных правил; реализация языка - это то, что либо интерпретируется, либо компилируется. Я мог бы взять язык, основная реализация которого скомпилирована (например, Go), и написать для него интерпретатор (и наоборот), но каждая программа, обрабатываемая интерпретатором, должна выдавать идентичный вывод, как при запуске программы через скомпилированную реализацию, и этот вывод должен соответствовать спецификации языка. Результаты PHP и Node.js фактически соответствуют спецификациям языков (как указывают некоторые другие ответы), и это не имеет ничего общего с тем фактом, что основные реализации этих языков интерпретируются; скомпилированные реализации языков по определению также должны давать те же результаты.

    Наглядным примером всего этого является Python, в котором есть как скомпилированные, так и интерпретируемые реализации. Запуск переведенной версии вашей программы в интерпретируемой реализации:

    >>> total = 0 
    >>> for i in xrange(1000000001):
    ...     total += i
    ... 
    >>> print total
    500000000500000000
    

    не должен, согласно определению Python, приводить к другому результату, чем его запуск в скомпилированной реализации:

    total = 0
    for i in xrange(1000000001):
        total += i
    
    print total
    
    500000000500000000
    
    11.08.2013

    33

    В Ruby эти функционально схожие решения (которые возвращают правильный ответ) требуют значительно разного времени для выполнения:

    $ time ruby -e "(1..1000000000).inject{|sum, n| sum + n}"
    real    1m26.005s
    user    1m26.010s
    sys 0m0.076s
    
    $ time ruby -e "1.upto(1000000000).inject(:+)"
    real    0m48.957s
    user    0m48.957s
    sys 0m0.045s
    
    $ ruby -v
    ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.8.0]
    
    05.08.2013

    34

    Javascript (и, возможно, PHP) представляет все числа как двойные и округляет их для целых значений. Это означает, что они имеют только 53 бита точности (вместо 64 бита, предоставляемых int64 и Java long), и приведут к ошибкам округления для больших значений.

    04.08.2013
  • не верно для PHP. посмотрите здесь 04.08.2013
  • верно для 32-битного PHP. Все, что больше PHP_INT_MAX, приводится к представлению с плавающей запятой. 04.08.2013

  • 35

    Как отмечали другие люди, самый быстрый способ выполнить этот расчет (независимо от языка) - использовать простую математическую функцию (вместо цикла с интенсивным использованием ЦП):

    number = 1000000000;
    result = (number/2) * (number+1);
    

    Тем не менее, вам все равно придется решать любые проблемы с 32/64 битными целыми числами / числами с плавающей запятой, в зависимости от языка.

    05.08.2013

    36

    И рубиновый:

    [15] pry(main)> (1..1000000000).inject(0) { |sum,e| sum + e }
    => 500000000500000000
    

    Кажется, получил правильный номер.

    05.08.2013
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..