ผลลัพธ์ที่ไม่คาดคิดเมื่อทำงานกับจำนวนเต็มที่มากในภาษาที่ตีความ


192

ฉันพยายามที่จะได้รับผลรวมของ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ดังนั้นฉันตัดสินใจลองภาษาอื่น

ไป

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

แต่มันใช้งานได้ดี! แล้วรหัส PHP และ Node.js ของฉันเป็นอย่างไร

บางทีนี่อาจเป็นปัญหาของการตีความภาษาและนั่นเป็นเหตุผลที่มันทำงานในภาษาที่คอมไพล์อย่าง Go? ถ้าใช่ภาษาอื่นที่ตีความเช่น Python และ Perl จะมีปัญหาเดียวกันหรือไม่


36
คุณต้องการสิ่งนี้: php.net/manual/en/book.bc.phpมิฉะนั้นคุณจะทุบหัวของคุณกับ IEEE 754 จนกว่านรกจะค้าง
tereško

5
สำหรับการจัดการจำนวนมากใน PHP (เช่น 64- บิต) ให้ใช้ฟังก์ชัน GMP ในกรณีนี้ gmp_add ()
เจฟฟรีย์

113
เพื่อประสิทธิภาพสูงสุดลูปของคุณควรเริ่มต้นที่ 1 แทนที่จะเป็น 0. : P
Graham Borland

55
รวม (1 ถึง N) = (N / 2) * (N + 1)
Phong

5
@Baba 0 นั้นยอดเยี่ยมสำหรับการคำนวณของคุณดังนั้นจึงไม่จำเป็นต้องมีการวนซ้ำเป็นพิเศษเพื่อเพิ่ม 0 ถึง 0
Brian Warshaw

คำตอบ:


155

Python ทำงานได้:

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

หรือ:

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

Python เป็นintระบบอัตโนมัติที่สนับสนุน 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

คุณใช้งานระบบ 32 หรือ 64 บิตได้หรือไม่?
บาบา

4
มันควรจะทำงานได้โดยไม่คำนึงถึง (32 vs 64 บิต) เนื่องจาก Python ints อัตโนมัติส่งเสริมความแม่นยำโดยพลการมากกว่าล้น อาจใช้เวลานานกว่านั้น
dawg

3
Python ในระบบใด ๆ จะทำงานในกรณีนี้เนื่องจาก Python เปลี่ยนเป็นจำนวนเต็มแบบยาวโดยอัตโนมัติหากจำเป็น และถ้ายังไม่พอก็จะเปลี่ยนเป็นจำนวนเต็มขนาดใหญ่เช่นกัน
Alok Singhal

12
@ 0x499602D2: นั่นมันช่างรุนแรงเหลือเกิน ตัว OP เองโหวตขึ้น เขาถามโดยเฉพาะว่านี่เป็นปัญหาที่คล้ายกันกับ Python หรือไม่ คำตอบไม่ได้ รหัสที่จะแสดงว่ามันไม่ได้เป็น WTH?
dawg

10
ตัวอย่างไพ ธ อนมีความยาวมากเกินไปเพียงใช้ผลรวม (xrange (int (1e9) +1)) (.... ผลรวมที่ทำงานบน iterables)
Jason Morgan

101

โค้ด Go ของคุณใช้เลขคณิตเลขจำนวนเต็มที่มีบิตเพียงพอที่จะให้คำตอบที่แน่นอน ไม่เคยแตะต้อง PHP หรือ Node.js แต่จากผลลัพธ์ที่ฉันสงสัยว่าคณิตศาสตร์ทำโดยใช้เลขทศนิยมและควรคาดว่าจะไม่ตรงกับจำนวนของขนาดนี้


46
อ๋อ 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
เนท

3
และใน NodeJS (และ JavaScript โดยทั่วไป) การดำเนินการทางคณิตศาสตร์ทั้งหมด (ยกเว้นการดำเนินการบิต) จะทำตัวราวกับว่าพวกเขาทำกับตัวเลขจุดลอยตัว ไม่ว่าพวกเขาจะเป็นความจริงหรือไม่นั้นขึ้นอยู่กับการตัดสินใจของเครื่องมือ JavaScript แต่ละตัว
Peter Olson

13
ในข้อกำหนดของจาวาสคริปต์ไม่มีประเภทจำนวนเต็ม ตัวเลขทั้งหมดเป็นจุดลอยตัว
toasted_flakes

8
@grasGendarme มี ES5 สเปคระบุแปลงต่างๆจำนวนเต็มและเอกสารที่ว่าพวกเขาจะเรียกว่ากะบิตเช่น กล่าวคือเบื้องหลัง , ชนิดจำนวนเต็มถูกใช้ใน Javascript, แต่ตัวดำเนินการทางคณิตศาสตร์ทั้งหมดแปลงตัวถูกดำเนินการของพวกเขาเป็นตัวเลขทศนิยมก่อนที่จะทำอะไรกับพวกเขา (ยกเว้นการเพิ่มประสิทธิภาพคอมไพเลอร์)
Peter Olson

2
นี่คือรหัสผมคิดว่ามัน messed ขึ้นเพราะผมใช้ float64 และไม่ int64 .. เพียงแค่ยืนยันว่ามีอะไรจะทำอย่างไรกับ 32 หรือ 64 บิต
บาบา

45

เหตุผลก็คือค่าของตัวแปรจำนวนเต็มของคุณsumเกินกว่าค่าสูงสุด และsumคุณได้รับเป็นผลมาจากการคำนวณเลขทศนิยมที่เกี่ยวข้องกับการปัดเศษ เนื่องจากคำตอบอื่น ๆ ไม่ได้กล่าวถึงขีด จำกัด ที่แน่นอนฉันจึงตัดสินใจโพสต์

ค่าจำนวนเต็มสูงสุดสำหรับ PHP สำหรับ:

  • รุ่น 32 บิตคือ2147483647
  • รุ่น 64 บิตคือ9223372036854775807

ดังนั้นหมายความว่าคุณกำลังใช้ CPU 32 บิตหรือ OS 32 บิตหรือ PHP ที่คอมไพล์ 32 บิต PHP_INT_MAXมันสามารถพบได้โดยใช้ sumจะคำนวณได้อย่างถูกต้องถ้าคุณทำมันบนเครื่อง 64 บิต

ค่าจำนวนเต็มสูงสุดใน JavaScript เป็น9007199254740992 ค่าอินทิกรัลที่แน่นอนที่ใหญ่ที่สุดที่คุณสามารถใช้ได้คือ 2 53 (นำมาจากคำถามนี้) sumเกินขีด จำกัด นี้

หากค่าจำนวนเต็มไม่เกินขีด จำกัด เหล่านี้แสดงว่าคุณดี มิฉะนั้นคุณจะต้องค้นหาไลบรารีจำนวนเต็มความแม่นยำโดยพลการ


28

นี่คือคำตอบใน 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 จัดทำโดยไม่สนับสนุนมาตรฐาน C99 อายุ 14 ปีดังนั้นการเรียกใช้สิ่งนี้เพื่อให้ทำงานใน Visual Studio นั้นเป็นมุมมองภาพคู่


3
MSVC ++ เป็นคอมไพเลอร์ C ++ และ C ++ ได้long longมาตรฐาน C ++ 11 มันเป็นส่วนเสริมของ MSVC ++ และ g ++ มาหลายปีแล้ว
MSalters

1
@MSalters ดังนั้นการมีคุณสมบัติ C ++ มันจะไม่ช่วยให้ทุกคนรวบรวมโปรแกรม C ตรงๆ ฉันไม่เคยลองเปลี่ยนจาก C เป็น C ++ ดังนั้นฉันไม่รู้ว่าวิธีแก้ปัญหานั้นจะได้ผลจริงหรือไม่
CyberSkull

19
และทาง GCC หรือ Clang ที่มีการปรับแต่งให้เหมาะสมนั้นเปลี่ยนวงทั้งหมดให้เป็นmovabsq $500000000500000000, %rsi
Tor Klingberg

3
เพียงหรือgcc -O3 clang -O3ฉันไม่ทราบชื่อของการเพิ่มประสิทธิภาพเฉพาะ โดยพื้นฐานแล้วคอมไพเลอร์สังเกตว่าผลลัพธ์ของการวนซ้ำไม่ได้ขึ้นอยู่กับอาร์กิวเมนต์ใด ๆ และคำนวณได้ในเวลารวบรวม
Tor Klingberg

1
ยาว C99 ยาวมีขนาดต่ำสุด 64 บิตและเท่าที่ฉันรู้คือ 64 บิตทั้งแพลตฟอร์ม 32 บิตและ 64 บิต ฉันไม่เห็นการสนับสนุนทั่วไปสำหรับ quad หรือ octo ints
Devin Lane

21

ฉันเดาว่าเมื่อผลรวมเกินความจุของพื้นเมืองint(2 31 -1 = 2,147,483,647), Node.js และ PHP เปลี่ยนไปเป็นตัวแทนจุดลอยตัวและคุณเริ่มได้รับข้อผิดพลาดรอบ ภาษาอย่าง Go อาจพยายามที่จะยึดติดกับรูปแบบจำนวนเต็ม (เช่นจำนวนเต็ม 64 บิต) ให้นานที่สุด (ถ้าจริง ๆ แล้วมันไม่ได้เริ่มต้นด้วย) เนื่องจากคำตอบนั้นพอดีกับจำนวนเต็ม 64 บิตการคำนวณจึงแม่นยำ


Node.js อย่างชัดเจนไม่มีประเภท int มันทำงานในรูปแบบลอย
greyfade

@greyfade - ใช่ฉันเดาว่าเป็นจริงของสภาพแวดล้อมที่สอดคล้องกับ EcmaScript ทั้งหมด
Ted Hopp

นั่นไม่ใช่ (2 ** 31 - 1) ใช่ไหม
Zachary Hunter

@ ZacharyHunter - แน่นอนมันคือ ขอบคุณที่รับข้อผิดพลาด
Ted Hopp

19

สคริปต์ 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

3
คุณใช้งานระบบ 32 หรือ 64 บิตได้หรือไม่?
บาบา

2
มันถูกประหารชีวิตในระบบ 64 บิต
มิเกล Prz

3
4.99999999067109e+017บน Perl v5.16.1 MSWin32-x86
Qtax

7
หากคุณต้องการตัวเลขใหญ่จริงๆใช้หรือbignum bigintทั้งสองเป็นโมดูลหลักนั่นคือพวกเขาจะติดตั้งกับ Perl v5.8.0 หรือสูงกว่า ดูhttp://perldoc.perl.org/bignum.htmlและhttp://perldoc.perl.org/bigint.html
shawnhcorey

ฉันได้ 500000000500000000 ทำงานบน PPC PPC แบบ 32 บิตรัน Perl 5.12.4
CyberSkull

17

คำตอบสำหรับเรื่องนี้คือ "น่าประหลาดใจ" ง่าย ๆ :

เฟิร์ส - เป็นส่วนใหญ่ของคุณอาจจะรู้ - ช่วง 32 บิตจำนวนเต็มจาก-2147483648เพื่อ2,147,483,647 แล้วจะเกิดอะไรขึ้นถ้า PHP ได้ผลลัพธ์นั่นคือสิ่งที่ใหญ่กว่านี้?

โดยปกติแล้วจะคาดหวังว่า "ล้น" ทันทีที่ก่อให้เกิด2,147,483,647 + 1จะกลายเป็น-2147483648 อย่างไรก็ตามนั่นไม่ใช่กรณี ถ้า 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 หรือไม่?

ฉันไม่คิดอย่างนั้น ฉันคิดว่านี่เป็นปัญหาของภาษาที่ไม่มีความปลอดภัยประเภท ในขณะที่จำนวนเต็มจำนวนเต็มดังที่กล่าวข้างต้นจะเกิดขึ้นในทุกภาษาที่ใช้ชนิดข้อมูลคงที่ภาษาที่ไม่มีความปลอดภัยของประเภทอาจพยายามจับกับประเภทข้อมูลอื่น ๆ อย่างไรก็ตามเมื่อพวกเขาชนชายแดน "ตามธรรมชาติ" (กำหนดโดยระบบ) พวกเขาอาจส่งคืนสิ่งใดก็ได้ แต่ผลลัพธ์ที่ถูกต้อง

อย่างไรก็ตามแต่ละภาษาอาจมีเธรดต่างกันสำหรับสถานการณ์ดังกล่าว


15

คำตอบอื่น ๆ แล้วอธิบายสิ่งที่เกิดขึ้นที่นี่ (ความแม่นยำจุดลอยตามปกติ)

ทางออกหนึ่งคือการใช้ประเภทจำนวนเต็มขนาดใหญ่พอหรือหวังว่าภาษาจะเลือกหนึ่งถ้าจำเป็น

อีกวิธีคือใช้อัลกอริธึมการสรุปที่รู้เกี่ยวกับปัญหาความแม่นยำและแก้ไขปัญหา ด้านล่างนี้คุณจะพบผลบวกเดียวกันกับครั้งแรกกับจำนวนเต็ม 64 บิตแล้วกับ 64 บิตลอยจุดแล้วใช้จุดลอยอีกครั้ง แต่มีขั้นตอนวิธีการบวก Kahan

เขียนด้วยภาษา 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

ผลรวม Kahan ให้ผลลัพธ์ที่สวยงาม แน่นอนว่าใช้เวลาในการคำนวณนานกว่ามาก ไม่ว่าคุณจะต้องการใช้มันขึ้นอยู่กับ) ประสิทธิภาพของคุณเทียบกับความต้องการที่แม่นยำและ b) วิธีที่ภาษาของคุณจัดการกับชนิดข้อมูลจำนวนเต็มเทียบกับจุดลอยตัว


@Baba มันเหมือนกับ Node.js / JavaScript ใน OP 500000000067109000 เทียบกับ 50000000006710,999,992 …ไม่มีความคิด
linac

บางทีบาบารู้สึกทึ่งกับการใช้จุดสำหรับตัวคั่นหลักพัน: โดยปกติแล้วภาษาอังกฤษคาดว่าจุลภาค คุณสามารถใช้เครื่องหมายขีดล่างเป็นค่าเฉลี่ยที่เป็นกลางกว่า
didierc

14

หากคุณมี PHP แบบ 32 บิตคุณสามารถคำนวณด้วย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- บิต


12

ในทับทิม:

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

พิมพ์500000000500000000แต่ใช้เวลา 4 นาทีใน 2.6 GHz Intel i7 ของฉัน


Magnuss และ Jaunty มีวิธีแก้ปัญหาทับทิมมากกว่า:

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

ในการเรียกใช้เกณฑ์มาตรฐาน:

$ 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

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

@agnuss: นั่นคือสิ่งที่ฉันคิดว่าฉันพยายามในตอนแรก แต่มันทำให้เกิดการรั่วไหลของหน่วยความจำขนาดใหญ่ ดูเหมือนว่าคุณจะทำงาน ...
cgenco

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 ภายใต้ประทุนซึ่งเป็นหนึ่งในไลบรารี่ที่มีความแม่นยำสูง


4

ใช้เวลานานในทับทิม แต่ให้คำตอบที่ถูกต้อง:

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

4

เพื่อให้ได้ผลลัพธ์ที่ถูกต้องใน php ฉันคิดว่าคุณต้องใช้ตัวดำเนินการทางคณิตศาสตร์ BC: http://php.net/manual/en/ref.bc.php

นี่คือคำตอบที่ถูกต้องใน Scala คุณต้องใช้ Longs ไม่เช่นนั้นคุณจะล้นตัวเลข:

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

3

จริงๆแล้วมันมีเคล็ดลับที่ยอดเยี่ยมสำหรับปัญหานี้

สมมติว่าเป็น 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


11
คุณคิดว่าเป็นไปได้ไหมที่จะเดินข้ามสะพานข้าม Pregel ในคาลินินกราดโดยไม่ต้องข้ามสะพานสองครั้ง? หลายคนพยายามแล้วล้มเหลว แต่ก็ยังไม่มีใครพิสูจน์ได้ว่าเป็นไปไม่ได้ นี่เป็นความท้าทายที่คุณจะได้รับการแก้ไขโดยเฉพาะ
jwg

3

สิ่งนี้จะให้ผลลัพธ์ที่ถูกต้องใน PHP โดยการบังคับจำนวนเต็ม

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

3

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 อาจทำ JITing บางอย่างภายในเพื่อให้ทำงานได้อย่างรวดเร็ว แต่ประสบการณ์แบบไดนามิกของการใช้รหัสทันทีเหมือนกัน

สามารถทำให้ง่ายขึ้นเป็น (เวลา (วนสำหรับ x จาก 1 ถึง 1000000000 ผลรวม x)) ฉันได้ความเร็ว 5 เท่าโดยเพิ่มการประกาศ: (เวลา (เฉพาะที่ (ประกาศ (เพิ่มประสิทธิภาพ (ความเร็ว 3)))) (ห่วงสำหรับ i ประเภท fixnum จาก 1 ถึง 1000000000 ผลรวมของประเภท fixnum))
huaiyuan

นี่เป็นข้อผิดพลาด อย่าให้คุณตาบอดด้วยภาษาอื่น! วิธีที่ถูกต้องในการเขียนในเสียงกระเพื่อมคือ: (defun sum-from-1-to-n (n) (/ (* n (1+ n)) 2)) (เวลา (sum-from-1-to-n 1000000000)) ใช้เวลา 14 microseconds (0.000014 วินาที) ในการเรียกใช้ ในช่วงเวลานั้นและมีแกน CPU ที่ใช้งานได้ 4 แกน 0 microseconds (0.000000 วินาที) ถูกใช้ในโหมดผู้ใช้ 0 microseconds (0.000000 วินาที) ถูกใช้ในโหมดระบบ -> 50000000050000000000
ข้อมูล

@informatimago: มันไม่ผิดพลาด ฉันกำลังคัดลอกสไตล์ลูปที่จำเป็นของคำถามและมีคนจำนวนมากที่ชี้ให้เห็นคำถามตัวเองกล่าวถึงมีวิธีที่ง่ายกว่าในการคำนวณ Chillax
postfuturist

3

ฉันไม่มีชื่อเสียงพอที่จะแสดงความคิดเห็นในคำตอบ Common LISP ของ @ postfuturist แต่สามารถเพิ่มประสิทธิภาพให้เสร็จสมบูรณ์ใน ~ 500ms ด้วย 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

3

แร็กเก็ต v 5.3.4 (MBP; เวลาเป็นมิลลิวินาที):

> (time (for/sum ([x (in-range 1000000001)]) x))
cpu time: 2943 real time: 2954 gc time: 0
500000000500000000

1
ลบคำตอบของฉันโพสต์ 6 นาทีหลังจากที่คุณเมื่อฉันสังเกตเห็นคุณ :)
Greg Hendershott

3

ทำงานได้ดีใน Rebol:

>> sum: 0
== 0

>> repeat i 1000000000 [sum: sum + i]
== 500000000500000000

>> type? sum
== integer!

นี่คือการใช้ Rebol 3 ซึ่งแม้จะเป็น 32 บิตที่รวบรวมจะใช้จำนวนเต็ม 64 บิต (ซึ่งแตกต่างจาก Rebol 2 ซึ่งใช้จำนวนเต็ม 32 บิต)


3

ฉันต้องการที่จะเห็นสิ่งที่เกิดขึ้นใน CF Script

<cfscript>
ttl = 0;

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

ฉันได้ 5.00000000067E + 017

นี่เป็นการทดลองที่ค่อนข้างดี ฉันค่อนข้างแน่ใจว่าฉันสามารถเขียนโค้ดนี้ได้ดีขึ้นเล็กน้อยด้วยความพยายามมากขึ้น


3

ActivePerl v5.10.1 บน Windows 32 บิต, Intel Core2duo 2.6:

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

ผล: 5.00000000067109e + 017 ใน 5 นาที

ด้วยสคริปต์ "use bigint" ใช้งานได้สองชั่วโมงและจะทำงานได้มากกว่า แต่ฉันหยุด ช้าเกินไป.


ทุกคนสามารถยืนยันได้ว่านั่นเป็นระยะเวลานานเท่าใดที่การเพิ่มภาพใหญ่ ๆ
jwg

3

เพื่อความสมบูรณ์ใน Clojure (สวยงาม แต่ไม่มีประสิทธิภาพมาก):

(reduce + (take 1000000000 (iterate inc 1))) ; => 500000000500000000

1
ประโยชน์เล็กน้อยเพียงเล็กน้อยที่คำตอบ $ MY_FAVOURITE_LANGUAGE คือหากพวกเขาให้ผล ...
jwg

@jwg ใช่ฉันขอโทษที่ไม่ได้รับสาย - คำตอบอัพเดท
Blacksad

3

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

2

หมวดหมู่ภาษาที่ตีความอื่น ๆ :

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 

ฉันวางการทดสอบไว้ใน proc เพื่อให้คอมไพล์ด้วยไบต์


2

สำหรับโค้ด PHP คำตอบอยู่ที่นี่ :

ขนาดของจำนวนเต็มขึ้นอยู่กับแพลตฟอร์มแม้ว่าค่าสูงสุดประมาณสองพันล้านเป็นค่าปกติ (นั่นคือการลงนาม 32 บิต) โดยทั่วไปแพลตฟอร์ม 64 บิตจะมีค่าสูงสุดประมาณ 9E18 PHP ไม่รองรับจำนวนเต็มที่ไม่ได้ลงนาม ขนาดของจำนวนเต็มสามารถกำหนดโดยใช้ค่าคงที่ PHP_INT_SIZE และค่าสูงสุดโดยใช้ค่าคงที่ PHP_INT_MAX ตั้งแต่ PHP 4.4.0 และ PHP 5.0.5


2

ท่าเรือ:

proc Main()

   local sum := 0, i

   for i := 0 to 1000000000
      sum += i
   next

   ? sum

   return

500000000500000000ผลการค้นหาใน (บนทั้ง windows / mingw / x86 และ osx / clang / x64)


2

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


2

สิ่งที่ตลก PHP 5.5.1 ให้ 499999999500000000 (ใน ~ 30 วินาที) ในขณะที่ Dart2Js ให้ 500000000067109000 (ซึ่งคาดว่าจะเกิดขึ้นเพราะมันเป็น JS ที่ได้รับการประหารชีวิต) CLI Dart ให้คำตอบที่ถูกต้อง ... ทันที


2

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.