สี่สี่เหลี่ยมด้วยกัน


19

ทฤษฎีบทสี่ตารางของลากรองจ์บอกเราว่าจำนวนธรรมชาติใด ๆ สามารถแทนได้ด้วยผลบวกของจำนวนสี่ตาราง งานของคุณคือการเขียนโปรแกรมที่ทำสิ่งนี้

อินพุต: จำนวนธรรมชาติ (ต่ำกว่า 1 พันล้าน)

ผลลัพธ์: ตัวเลขสี่ตัวที่มีกำลังสองบวกกับจำนวนนั้น (ลำดับไม่สำคัญ)

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

ตัวอย่างอินพุต

123456789

เอาต์พุตตัวอย่าง (ใช้ได้ทั้งคู่)

10601 3328 2 0
10601 3328 2

ฉันอาจใช้กำลังดุร้ายแม้ว่าจะทำให้รหัสของฉันสั้นลงหรือไม่
Martin Ender

@ m.buettner ใช่ แต่ควรจัดการจำนวนมาก
qwr

@ m.buettner อ่านโพสต์หมายเลขธรรมชาติต่ำกว่า 1 พันล้าน
qwr

อาขอโทษที่มองข้าม
Martin Ender

2
@Dennis Natural numbers ในกรณีนี้ไม่รวม 0.
qwr

คำตอบ:


1

CJam, 50 ไบต์

li:NmF0=~2/#:J(_{;)__*N\-[{_mqJ/iJ*__*@\-}3*])}g+p

คำตอบที่สามของฉัน (และสุดท้ายฉันสัญญา) วิธีนี้ขึ้นอยู่กับคำตอบของ Primo เป็นอย่างมาก

ลองใช้ออนไลน์ในล่าม CJam

การใช้

$ cjam 4squares.cjam <<< 999999999
[189 31617 567 90]

พื้นหลัง

  1. หลังจากได้เห็นอัลกอริทึมที่อัปเดตของพรีโม่ฉันต้องดูว่าการใช้ CJam จะได้คะแนนอย่างไร

    li{W):W;:N4md!}g;Nmqi)_{;(__*N\-[{_mqi__*@\-}3*])}g+2W#f*p
    

    เพียง 58 ไบต์! อัลกอริธึมนี้ทำงานได้ในเวลาเกือบคงที่และไม่มีการเปลี่ยนแปลงมากนักสำหรับค่าNต่างๆ มาเปลี่ยน ...

  2. แทนที่จะเริ่มfloor(sqrt(N))และลดจำนวนเราสามารถเริ่มต้น1และเพิ่มขึ้นได้ สิ่งนี้จะช่วยประหยัด 4 ไบต์

    li{W):W;:N4md!}g;0_{;)__*N\-[{_mqi__*@\-}3*])}g+2W#f*p
    
  3. แทนที่จะแสดงNเป็น4**a * bเราสามารถแสดงเป็นp**(2a) * b- ที่ไหนpเป็นปัจจัยสำคัญที่สุดของN- เพื่อบันทึก 1 ไบต์เพิ่มเติม

    li_mF0=~2/#:J_*/:N!_{;)__*N\-[{_mqi__*@\-}3*])}g+Jf*p
    
  4. การปรับเปลี่ยนก่อนหน้านี้ช่วยให้เราสามารถเปลี่ยนแปลงเล็กน้อยในการดำเนินการ (โดยไม่ต้องสัมผัสขั้นตอนวิธีการของตัวเอง): แทนที่จะหารNโดยp**(2a)และคูณแก้ปัญหาโดยp**aเราโดยตรงสามารถ จำกัด p**aการแก้ปัญหาที่เป็นไปได้หลายรายการ สิ่งนี้จะช่วยประหยัดได้อีก 2 ไบต์

    li:NmF0=~2/#:J!_{;J+__*N\-[{_mqJ/iJ*__*@\-}3*])}g+`
    
  5. ไม่ จำกัด จำนวนเต็มแรกให้ทวีคูณp**aบันทึกเป็นไบต์เพิ่มเติม

    li:NmF0=~2/#:J(_{;)__*N\-[{_mqJ/iJ*__*@\-}3*])}g+`
    

ขั้นตอนวิธีสุดท้าย

  1. ค้นหาaและbเช่นว่าN = p**(2a) * bที่bไม่ได้เป็นหลายp**2และเป็นปัจจัยที่สำคัญที่สุดของpN

  2. j = p**aชุด

  3. k = floor(sqrt(N - j**2) / A) * Aชุด

  4. l = floor(sqrt(N - j**2 - k**2) / A) * Aชุด

  5. m = floor(sqrt(N - j**2 - k**2 - l**2) / A) * Aชุด

  6. หากN - j**2 - k**2 - l**2 - m**2 > 0ตั้งค่าj = j + 1และกลับไปที่ขั้นตอนที่ 3

สามารถดำเนินการได้ดังนี้:

li:N          " Read an integer from STDIN and save it in “N”.                        ";
mF            " Push the factorization of “N”. Result: [ [ p1 a1 ] ... [ pn an ] ]    ";
0=~           " Push “p1” and “a1”. “p1” is the smallest prime divisor of “N”.        ";
2/#:J         " Compute p1**(a1/2) and save the result “J”.                           ";
(_            " Undo the first two instructions of the loop.                          ";
{             "                                                                       ";
  ;)_         " Pop and discard. Increment “J” and duplicate.                         ";
  _*N\-       " Compute N - J**2.                                                     ";
  [{          "                                                                       ";
    _mqJ/iJ*  " Compute K = floor(sqrt(N - J**2)/J)*J.                                ";
    __*@      " Duplicate, square and rotate. Result: K   K**2   N - J**2             ";
    \-        " Swap and subtract. Result: K   N - J**2 - K**2                        ";
  }3*]        " Do the above three times and collect in an array.                     ";
  )           " Pop the array. Result: N - J**2 - K**2 - L**2 - M**2                  ";
}g            " If the result is zero, break the loop.                                ";
+p            " Unshift “J” in [ K L M ] and print a string representation.           ";

มาตรฐาน

ฉันใช้งานทั้ง 5 เวอร์ชันในจำนวนเต็มบวกทั้งหมดได้ถึง 999,999,999 ใน Intel Core i7-3770 ของฉันวัดเวลาดำเนินการและนับจำนวนการทำซ้ำที่จำเป็นในการค้นหาวิธีแก้ไข

ตารางต่อไปนี้แสดงจำนวนการทำซ้ำโดยเฉลี่ยและเวลาดำเนินการสำหรับจำนวนเต็มเดียว:

Version               |    1    |    2    |    3    |    4    |    5
----------------------+---------+---------+---------+---------+---------
Number of iterations  |  4.005  |  28.31  |  27.25  |  27.25  |  41.80
Execution time [µs]   |  6.586  |  39.69  |  55.10  |  63.99  |  88.81
  1. ด้วยการวนซ้ำเพียง 4 ครั้งและ 6.6 ไมโครวินาทีต่อจำนวนเต็มอัลกอริทึมของพรีโม่รวดเร็วอย่างเหลือเชื่อ

  2. การเริ่มต้นที่floor(sqrt(N))สมเหตุสมผลมากขึ้นเนื่องจากสิ่งนี้ทำให้เรามีค่าน้อยลงสำหรับผลรวมของสามกำลังสองที่เหลือ ตามที่คาดไว้การเริ่มต้นที่ 1 จะช้ากว่ามาก

  3. นี่เป็นตัวอย่างคลาสสิกของแนวคิดที่ดีที่นำมาใช้ไม่ดี ที่จริงการลดขนาดรหัสที่เราพึ่งพาซึ่งมีตัวประกอบจำนวนเต็มmF Nแม้ว่าเวอร์ชัน 3 จะต้องการการวนซ้ำน้อยกว่าเวอร์ชัน 2 แต่ในทางปฏิบัติจะช้ากว่ามาก

  4. แม้ว่าอัลกอริทึมจะไม่เปลี่ยนแปลง แต่เวอร์ชัน 4 ช้ากว่ามาก นี่เป็นเพราะมันทำการหารทศนิยมเพิ่มเติมและการคูณจำนวนเต็มในแต่ละการวนซ้ำ

  5. สำหรับอินพุทN = p**(2a) ** bอัลกอริทึม 5 จะต้องมี(k - 1) * p**a + 1การวนซ้ำโดยที่kจำนวนอัลกอริทึมการวนซ้ำ 4 นั้นต้องการ หากk = 1หรือa = 0สิ่งนี้ไม่สร้างความแตกต่าง

    อย่างไรก็ตามการป้อนข้อมูลใด ๆ ของแบบฟอร์ม4**a * (4**c * (8 * d + 7) + 1)อาจทำงานได้ไม่ดีนัก สำหรับค่าเริ่มต้นj = p**a, N - 4**a = 4**(a + c) * (8 * d + 7)ดังนั้นจึงไม่สามารถแสดงเป็นผลรวมของสามสี่เหลี่ยม ดังนั้นk > 1และอย่างน้อยp**aต้องมีการวนซ้ำ

    N < 1,000,000,000โชคดีที่ขั้นตอนวิธีการเดิมพรีโม่เป็นอย่างไม่น่าเชื่ออย่างรวดเร็วและ กรณีที่แย่ที่สุดที่ฉันสามารถหาได้ด้วยมือคือ265,289,728 = 4**10 * (4**1 * (7 * 8 + 7) + 1)ซึ่งต้องมีการทำซ้ำ 6,145 ครั้ง เวลาดำเนินการน้อยกว่า 300 ms ในเครื่องของฉัน โดยเฉลี่ยแล้วรุ่นนี้ช้ากว่าการนำอัลกอริธึมของไพรม์ไป 13.5 เท่า


"แทนที่จะแสดงออกNว่า4**a * bเราสามารถแสดงได้p**(2a) * b" นี่คือการปรับปรุงจริง ฉันชอบที่จะรวมสิ่งนี้ไว้ แต่มันก็นานกว่ามาก (อุดมคติคือการค้นหาตัวคูณที่สมบูรณ์แบบที่สุด) "เริ่มต้นด้วย 1 และการเพิ่มจะบันทึก 4 ไบต์" ช้ากว่านี้แน่นอน รันไทม์สำหรับช่วงที่กำหนดคือ 4-5 เท่านาน "จำนวนเต็มบวกทั้งหมดสูงถึง 999,999,999 ใช้เวลา 24.67 ชั่วโมงให้เวลาดำเนินการเฉลี่ย 0.0888 มิลลิวินาทีต่อจำนวนเต็ม" Perl เพียงเอา 2.5 ชั่วโมงไปกระทืบทั้งช่วงและการแปลงูหลามเป็น 10 เท่าได้เร็วขึ้น;)
พรีโม่

@primo: ใช่คุณพูดถูก การหารด้วยp**aเป็นการปรับปรุง แต่มันเล็กไปหน่อย หารด้วยตารางปัจจัยที่สมบูรณ์แบบที่ใหญ่ที่สุดที่ทำให้ใหญ่แตกต่างจากเมื่อเริ่มต้น 1; มันยังคงปรับปรุงเมื่อเริ่มจากส่วนที่เป็นจำนวนเต็มของสแควร์รูท การใช้งานจะมีค่าใช้จ่ายเพิ่มขึ้นอีกสองไบต์เท่านั้น ดูเหมือนว่าเวลาในการประหารชีวิตของฉันจะไม่ได้รับการพัฒนามากนักไม่ใช่ CJam ฉันจะรันการทดสอบสำหรับอัลกอริทึมใหม่ทั้งหมด (รวมถึงการทดสอบที่คุณเสนอ) นับการวนซ้ำมากกว่าการวัดเวลากำแพง ลองมาดูว่าระยะเวลาที่จะใช้เวลา ...
เดนนิส

การค้นหาตัวประกอบกำลังสองที่ใหญ่ที่สุดมีค่าใช้จ่ายเพิ่มอีก 2 ไบต์! เวทมนตร์ชนิดใดกันนี่คืออะไร?
โม่

@primo: หากจำนวนเต็มอยู่ใน stack ให้1\สลับเป็น 1 (ตัวสะสม) mFผลักตัวแยกและ{~2/#*}/ยกระดับตัวประกอบสำคัญทุกตัวให้เป็นเลขชี้กำลังหารด้วยสองแล้วคูณด้วยตัวสะสม สำหรับการนำอัลกอริทึมของคุณไปใช้งานโดยตรงนั้นจะเพิ่ม 2 ไบต์เท่านั้น ความแตกต่างเล็ก ๆ เป็นหลักเนื่องจากเป็นวิธีที่น่าอึดอัดใจฉันได้ไปหาตัวแทนของ 4, ตั้งแต่ CJam ไม่ (ดูเหมือนจะ) มีในขณะที่วง ...
เดนนิส

อย่างไรก็ตามมาตรฐานเสร็จสิ้น จำนวนการทำซ้ำทั้งหมดที่ต้องใช้ในการแยกจำนวนเต็ม 1,000,000 จำนวนเต็มโดยไม่ต้องหาตัวประกอบกำลังสองที่ใหญ่ที่สุดคือ 4,004,829,417 โดยใช้เวลาดำเนินการ 1.83 การหารด้วยตัวประกอบกำลังสองที่ใหญ่ที่สุดจะลดจำนวนการวนซ้ำเป็น 3,996,724,799 แต่จะเพิ่มเวลาเป็น 6.7 ชั่วโมง ดูเหมือน factorizing ต้องใช้เวลามากขึ้นกว่าการหาสี่เหลี่ยม ...
เดนนิส

7

FRACTRAN: 156 98 เศษส่วน

เนื่องจากนี่เป็นปัญหาทฤษฎีจำนวนคลาสสิควิธีที่ดีกว่าในการแก้ปัญหานี้คือการใช้ตัวเลข!

37789/221 905293/11063 1961/533 2279/481 57293/16211 2279/611 53/559 1961/403 53/299 13/53 1/13 6557/262727 6059/284321 67/4307 67/4661 6059/3599 59/83 1/59 14279/871933 131/9701 102037079/8633 14017/673819 7729/10057 128886839/8989 13493/757301 7729/11303 89/131 1/89 31133/2603 542249/19043 2483/22879 561731/20413 2483/23701 581213/20687 2483/24523 587707/21509 2483/24797 137/191 1/137 6215941/579 6730777/965 7232447/1351 7947497/2123 193/227 31373/193 23533/37327 5401639/458 229/233 21449/229 55973/24823 55973/25787 6705901/52961 7145447/55973 251/269 24119/251 72217/27913 283/73903 281/283 293/281 293/28997 293/271 9320827/58307 9831643/75301 293/313 28213/293 103459/32651 347/104807 347/88631 337/347 349/337 349/33919 349/317 12566447/68753 13307053/107143 349/367 33197/349 135199/38419 389/137497 389/119113 389/100729 383/389 397/383 397/39911 397/373 1203/140141 2005/142523 2807/123467 4411/104411 802/94883 397/401 193/397 1227/47477 2045/47959 2863/50851 4499/53743 241/409 1/241 1/239

ใช้เวลาในการป้อนข้อมูลในรูปแบบ 2 n × 193 และผล 3 × 5 × 7 × 11 d อาจใช้เวลา 3 นาทีถ้าคุณมีล่ามที่ดีจริงๆ อาจจะ.

... ไม่เป็นไร นี่ดูเหมือนจะเป็นปัญหาที่สนุกมากใน FRACTRAN ที่ฉันต้องลอง เห็นได้ชัดว่านี่ไม่ใช่คำตอบที่ถูกต้องสำหรับคำถามเพราะมันไม่ได้ทำตามข้อกำหนดของเวลา (มันเป็นกองกำลังที่ดุร้าย) และมันก็แทบจะตีกอล์ฟได้ แต่ฉันคิดว่าฉันจะโพสต์ที่นี่เพราะไม่ใช่คำถาม Codegolf ทุกวัน สามารถทำได้ใน FRACTRAN;)

เปรย

รหัสเทียบเท่ากับ pseudo-Python ต่อไปนี้:

a, b, c, d = 0, 0, 0, 0

def square(n):
    # Returns n**2

def compare(a, b):
    # Returns (0, 0) if a==b, (1, 0) if a<b, (0, 1) if a>b

def foursquare(a, b, c, d):
    # Returns square(a) + square(b) + square(c) + square(d)

while compare(foursquare(a, b, c, d), n) != (0, 0):
    d += 1

    if compare(c, d) == (1, 0):
        c += 1
        d = 0

    if compare(b, c) == (1, 0):
        b += 1
        c = 0
        d = 0

    if compare(a, b) == (1, 0):
        a += 1
        b = 0
        c = 0
        d = 0

7

Mathematica 61 66 51

แสดงวิธีการสามวิธี มีเพียงแนวทางแรกเท่านั้นที่ตรงตามเวลาที่กำหนด


1-FindInstance (51 ถ่าน)

นี่จะส่งคืนสมการเดียว

FindInstance[a^2 + b^2 + c^2 + d^2 == #, {a, b, c, d}, Integers] &

ตัวอย่างและการกำหนดเวลา

FindInstance[a^2 + b^2 + c^2 + d^2 == 123456789, {a, b, c, d}, Integers] // AbsoluteTiming

{0.003584, {{a -> 2600, b -> 378, c -> 10468, d -> 2641}}}

FindInstance[a^2 + b^2 + c^2 + d^2 == #, {a, b, c, d}, Integers] &[805306368]

{0.004437, {{a -> 16384, b -> 16384, c -> 16384, d -> 0}}


2 IntegerPartitions

วิธีนี้ใช้งานได้ แต่ช้าเกินไปที่จะตอบสนองความต้องการด้านความเร็ว

f@n_ := Sqrt@IntegerPartitions[n, {4}, Range[0, Floor@Sqrt@n]^2, 1][[1]]

Range[0, Floor@Sqrt@n]^2คือชุดของช่องสี่เหลี่ยมทั้งหมดที่น้อยกว่ารากที่สองของn(ตารางที่ใหญ่ที่สุดที่เป็นไปได้ในพาร์ติชัน)

{4}ต้องการพาร์ทิชันจำนวนเต็มnประกอบด้วย 4 องค์ประกอบจากชุดของสี่เหลี่ยมที่กล่าวถึงข้างต้น

1ภายในฟังก์ชั่นIntegerPartitionsส่งกลับโซลูชั่นแรก

[[1]]ลบวงเล็บปีกกาด้านนอก; โซลูชันถูกส่งคืนเป็นชุดหนึ่งองค์ประกอบ


f[123456]

{348, 44, 20, 4}


3 PowerRepresentations

PowerRepresentationsผลตอบแทนทั้งหมดของโซลูชั่นเพื่อปัญหา 4 สี่เหลี่ยม นอกจากนี้ยังสามารถแก้ปัญหาได้สำหรับผลรวมของพลังอื่น ๆ

PowersRepresentations ส่งคืนภายใน 18 วินาทีวิธี 181 ในการแสดง 123456789 เป็นผลรวมของ 4 กำลังสอง:

n= 123456;
PowersRepresentations[n, 4, 2] //AbsoluteTiming

รัชทายาท

อย่างไรก็ตามมันช้าเกินไปสำหรับจำนวนเงินอื่น ๆ


ว้าว Mathematica ใช้กำลังดุร้ายอย่างรวดเร็ว IntegerPartitions กำลังทำสิ่งที่ฉลาดกว่าการลองชุดค่าผสมทุกอย่างเช่น DFT convolutions ในเซตหรือไม่? รายละเอียดขอตัวเลขโดยวิธีการที่ไม่ได้สี่เหลี่ยมของพวกเขา
xnor

ผมคิดว่า Mathematica ใช้กำลังดุร้าย IntegerPartitionsแต่มันอาจจะมีการเพิ่มประสิทธิภาพ ในขณะที่คุณสามารถดูจากการกำหนดเวลา, ความเร็วแตกต่างกันมากขึ้นอยู่กับว่า (ใหญ่ที่สุด) nหมายเลขแรกอยู่ใกล้กับรากที่สองของ ขอขอบคุณที่ติดตามการละเมิดข้อมูลจำเพาะในรุ่นก่อนหน้า
DavidC

คุณสามารถสร้างมาตรฐานf[805306368]หรือไม่ โดยไม่ต้องหารด้วยพลังของ 4 ก่อนวิธีแก้ปัญหาของฉันใช้เวลา 0.05 s สำหรับ 999999999; ฉันได้ยกเลิกมาตรฐานสำหรับ 805306368 หลังจาก 5 นาที ...
เดนนิส

f[805306368]กลับมา{16384, 16384, 16384}หลังจาก 21 นาที ฉันใช้ {3} แทน {4} ความพยายามในการแก้ไขด้วยผลรวมของสี่เหลี่ยมที่ไม่ใช่ศูนย์ 4 ไม่สำเร็จหลังจากใช้งานเป็นเวลาหลายชั่วโมง
DavidC

ฉันไม่สามารถเข้าถึง Mathematica แต่จากสิ่งที่ฉันอ่านในศูนย์เอกสารIntegerPartitions[n,4,Range[Floor@Sqrt@n]^2ควรทำงานได้ดี อย่างไรก็ตามฉันไม่คิดว่าคุณควรใช้วิธีที่ 1 สำหรับคะแนนของคุณเนื่องจากไม่เป็นไปตามเวลาที่ระบุในคำถาม
Dennis

7

Perl - 116 ไบต์ 87 ไบต์ (ดูอัปเดตด้านล่าง)

#!perl -p
$.<<=1,$_>>=2until$_&3;
{$n=$_;@a=map{$n-=$a*($a-=$_%($b=1|($a=0|sqrt$n)>>1));$_/=$b;$a*$.}($j++)x4;$n&&redo}
$_="@a"

นับ Shebang เป็นหนึ่งไบต์เพิ่มบรรทัดใหม่เพื่อสุขภาพจิตแนวนอน

บางสิ่งบางอย่างมาจากการรวม การส่ง

เฉลี่ย (ที่เลวร้ายที่สุด?) ความซับซ้อนกรณีที่ดูเหมือนว่าจะเป็นO (log n) O (n 0.07 ) ไม่มีอะไรที่ฉันได้พบวิ่งช้ากว่า 0.001s และผมได้ตรวจสอบในช่วงที่ทั้งหมดจากการ900000000 - 999999999 หากคุณพบสิ่งใดที่ใช้เวลานานกว่านั้นอย่างมาก ~ 0.1 วินาทีหรือมากกว่านั้นโปรดแจ้งให้เราทราบ


ตัวอย่างการใช้งาน

$ echo 123456789 | timeit perl four-squares.pl
11110 157 6 2

Elapsed Time:     0:00:00.000

$ echo 1879048192 | timeit perl four-squares.pl
32768 16384 16384 16384

Elapsed Time:     0:00:00.000

$ echo 999950883 | timeit perl four-squares.pl
31621 251 15 4

Elapsed Time:     0:00:00.000

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

หากคุณต้องการทดสอบช่วงของค่าต่างๆคุณสามารถใช้สคริปต์ต่อไปนี้:

use Time::HiRes qw(time);

$t0 = time();

# enter a range, or comma separated list here
for (1..1000000) {
  $t1 = time();
  $initial = $_;
  $j = 0; $i = 1;
  $i<<=1,$_>>=2until$_&3;
  {$n=$_;@a=map{$n-=$a*($a-=$_%($b=1|($a=0|sqrt$n)>>1));$_/=$b;$a*$i}($j++)x4;$n&&redo}
  printf("%d: @a, %f\n", $initial, time()-$t1)
}
printf('total time: %f', time()-$t0);

ดีที่สุดเมื่อไปยังไฟล์ ช่วงนี้1..1000000ใช้เวลาประมาณ 14 วินาทีบนคอมพิวเตอร์ของฉัน (71,000 ค่าต่อวินาที) และช่วงนั้น999000000..1000000000ใช้เวลาประมาณ 20 วินาที (50,000 ค่าต่อวินาที) ซึ่งสอดคล้องกับความซับซ้อนเฉลี่ยO (บันทึก n)


ปรับปรุง

แก้ไข : ปรากฎว่าขั้นตอนวิธีนี้จะคล้ายกับหนึ่งที่ได้ถูกนำมาใช้เครื่องคิดเลขจิตสำหรับศตวรรษที่อย่างน้อย

ตั้งแต่เดิมโพสต์ผมได้ตรวจสอบทุกค่าในช่วงตั้งแต่1..1000000000 พฤติกรรม 'กรณีที่เลวร้ายที่สุด' ถูกแสดงด้วยค่า699731569ซึ่งทดสอบรวมทั้งหมด190ชุดก่อนที่จะถึงวิธีแก้ปัญหา หากคุณพิจารณา190จะเป็นค่าคงที่ขนาดเล็ก - และแน่นอนฉันทำ - พฤติกรรมกรณีที่เลวร้ายในช่วงที่จำเป็นต้องได้รับการพิจารณาO (1) นั่นคือเร็วเท่าที่ค้นหาวิธีแก้ปัญหาจากตารางยักษ์และโดยเฉลี่ยอาจเร็วกว่า

อีกสิ่งหนึ่งที่แม้ว่า หลังจาก190การทำซ้ำแล้วสิ่งที่มีขนาดใหญ่กว่า1,444,00ยังไม่ได้ทำมันเกินกว่าผ่านครั้งแรก ตรรกะสำหรับการสำรวจเส้นทางแรกที่ไม่มีค่า - มันไม่ได้ใช้ โค้ดด้านบนสามารถย่อให้สั้นลงได้เล็กน้อย:

#!perl -p
$.*=2,$_/=4until$_&3;
@a=map{$=-=$%*($%=$=**.5-$_);$%*$.}$j++,(0)x3while$=&&=$_;
$_="@a"

ซึ่งจะทำการส่งผ่านครั้งแรกของการค้นหาเท่านั้น เราจำเป็นต้องยืนยันว่าไม่มีค่าต่ำกว่า144400ที่จำเป็นต้องผ่านรอบที่สองแม้ว่า:

for (1..144400) {
  $initial = $_;

  # reset defaults
  $.=1;$j=undef;$==60;

  $.*=2,$_/=4until$_&3;
  @a=map{$=-=$%*($%=$=**.5-$_);$%*$.}$j++,(0)x3while$=&&=$_;

  # make sure the answer is correct
  $t=0; $t+=$_*$_ for @a;
  $t == $initial or die("answer for $initial invalid: @a");
}

กล่าวโดยย่อคือช่วง1..1000000000มีวิธีแก้ปัญหาเวลาเกือบคงที่และคุณกำลังดูอยู่


อัพเดทแล้ว

@Dennisและฉันได้ทำการปรับปรุงหลายขั้นตอนวิธีนี้ คุณสามารถติดตามความคืบหน้าในความคิดเห็นด้านล่างและการสนทนาที่ตามมาหากคุณสนใจ จำนวนเฉลี่ยของการวนซ้ำสำหรับช่วงที่ต้องการลดลงจากเพียง4ลงเหลือ1.229และเวลาที่ใช้ในการทดสอบค่าทั้งหมดสำหรับ1..1000000000ได้รับการปรับปรุงจาก 18m 54s เป็น 2m 41s กรณีที่เลวร้ายที่สุดต้องมีการวนซ้ำ190ครั้ง; กรณีที่เลวร้ายที่สุดในขณะนี้, 854382778 , ต้องการเพียง21

รหัส Python สุดท้ายคือต่อไปนี้:

from math import sqrt

# the following two tables can, and should be pre-computed

qqr_144 = set([  0,   1,   2,   4,   5,   8,   9,  10,  13,
                16,  17,  18,  20,  25,  26,  29,  32,  34,
                36,  37,  40,  41,  45,  49,  50,  52,  53,
                56,  58,  61,  64,  65,  68,  72,  73,  74,
                77,  80,  81,  82,  85,  88,  89,  90,  97,
                98, 100, 101, 104, 106, 109, 112, 113, 116,
               117, 121, 122, 125, 128, 130, 133, 136, 137])

# 10kb, should fit entirely in L1 cache
Db = []
for r in range(72):
  S = bytearray(144)
  for n in range(144):
    c = r

    while True:
      v = n - c * c
      if v%144 in qqr_144: break
      if r - c >= 12: c = r; break
      c -= 1

    S[n] = r - c
  Db.append(S)

qr_720 = set([  0,   1,   4,   9,  16,  25,  36,  49,  64,  81, 100, 121,
              144, 145, 160, 169, 180, 196, 225, 241, 244, 256, 265, 289,
              304, 324, 340, 361, 369, 385, 400, 409, 436, 441, 481, 484,
              496, 505, 529, 544, 576, 580, 585, 601, 625, 640, 649, 676])

# 253kb, just barely fits in L2 of most modern processors
Dc = []
for r in range(360):
  S = bytearray(720)
  for n in range(720):
    c = r

    while True:
      v = n - c * c
      if v%720 in qr_720: break
      if r - c >= 48: c = r; break
      c -= 1

    S[n] = r - c
  Dc.append(S)

def four_squares(n):
  k = 1
  while not n&3:
    n >>= 2; k <<= 1

  odd = n&1
  n <<= odd

  a = int(sqrt(n))
  n -= a * a
  while True:
    b = int(sqrt(n))
    b -= Db[b%72][n%144]
    v = n - b * b
    c = int(sqrt(v))
    c -= Dc[c%360][v%720]
    if c >= 0:
      v -= c * c
      d = int(sqrt(v))

      if v == d * d: break

    n += (a<<1) - 1
    a -= 1

  if odd:
    if (a^b)&1:
      if (a^c)&1:
        b, c, d = d, b, c
      else:
        b, c = c, b

    a, b, c, d = (a+b)>>1, (a-b)>>1, (c+d)>>1, (c-d)>>1

  a *= k; b *= k; c *= k; d *= k

  return a, b, c, d

สิ่งนี้ใช้ตารางการแก้ไขที่คำนวณล่วงหน้าสองตารางขนาด 10kb หนึ่งตารางและอีก 253kb รหัสข้างต้นรวมถึงฟังก์ชั่นเครื่องกำเนิดสำหรับตารางเหล่านี้แม้ว่าสิ่งเหล่านี้อาจจะต้องคำนวณในเวลารวบรวม

รุ่นที่มีตารางการแก้ไขที่มีขนาดเล็กกว่าสามารถดูได้ที่นี่: http://codepad.org/1ebJC2OVเวอร์ชันนี้ต้องใช้การเฉลี่ย1.620การทำซ้ำต่อเทอมโดยมีกรณีที่เลวร้ายที่สุดที่38และช่วงทั้งหมดทำงานในประมาณ 21 วินาที เวลาเล็กน้อยถูกสร้างขึ้นโดยใช้ bitwise andสำหรับการแก้ไขbแทนโมดูโล


ปรับปรุง

แม้ค่ามีแนวโน้มที่จะสร้างโซลูชันมากกว่าค่าคี่
บทความการคำนวณทางจิตที่เชื่อมโยงกับบันทึกก่อนหน้านี้ว่าหากหลังจากลบปัจจัยทั้งหมดของสี่ค่าที่จะถูกย่อยสลายเป็นเลขคู่ค่านี้สามารถแบ่งได้สองค่าและวิธีการสร้างใหม่:

แม้ว่าสิ่งนี้อาจสมเหตุสมผลสำหรับการคำนวณทางจิต (ค่าที่น้อยกว่ามักจะคำนวณได้ง่ายกว่า) แต่ก็ไม่สมเหตุสมผลนัก หากคุณใช้256สุ่ม4 -tuples และตรวจสอบผลรวมของสแควร์สโมดูโล8คุณจะพบว่าค่า1 , 3 , 5และ7มีค่าเฉลี่ย32ครั้ง อย่างไรก็ตามค่า2และ6มีค่าถึง48ครั้ง การคูณค่าคี่ด้วย2จะพบวิธีแก้ปัญหาโดยเฉลี่ยน้อยกว่า33%การทำซ้ำ การฟื้นฟูมีดังต่อไปนี้:

ต้องใช้ความระมัดระวังว่าaและbมีความเท่าเทียมกันเท่ากันเช่นเดียวกับcและdแต่หากพบวิธีการแก้ปัญหาทั้งหมดคำสั่งที่เหมาะสมจะรับประกันว่าจะมีอยู่จริง

ไม่จำเป็นต้องตรวจสอบเส้นทางที่เป็นไปไม่ได้
หลังจากเลือกค่าที่สองแล้วbอาจเป็นไปไม่ได้เลยที่วิธีแก้ปัญหาจะมีอยู่เนื่องจากมีสารตกค้างกำลังสองที่เป็นไปได้สำหรับโมดูโลที่กำหนด แทนที่จะทำการตรวจสอบต่อไปหรือย้ายไปที่การวนซ้ำครั้งถัดไปค่าของbสามารถ 'แก้ไข' โดยการลดค่าลงด้วยจำนวนที่น้อยที่สุดซึ่งอาจนำไปสู่การแก้ปัญหา สองตารางการแก้ไขเก็บค่าเหล่านี้หนึ่งสำหรับ , และอื่น ๆ สำหรับค การใช้โมดูโล่ที่สูงขึ้น (แม่นยำยิ่งขึ้นการใช้โมดูโล่ที่มีเศษกำลังสองน้อยกว่า) จะส่งผลให้การปรับปรุงดีขึ้น ค่าaไม่ต้องการการแก้ไขใด ๆ โดยการแก้ไขnให้เป็นค่าทั้งหมดของaถูกต้อง


1
มันช่างเหลือเชื่อ! อัลกอริทึมสุดท้ายน่าจะเป็นที่ง่ายที่สุดของทุกคำตอบที่ยังมีการทำซ้ำ 190 ทั้งหมดจะใช้เวลา ...
เดนนิส

@Dennis ฉันจะประหลาดใจมากถ้ามันไม่ได้พูดถึงที่อื่น ดูเหมือนง่ายเกินไปที่จะมองข้าม
primo

1. ฉันอยากรู้อยากเห็น: ค่าการทดสอบใด ๆ ในการวิเคราะห์ความซับซ้อนของคุณต้องใช้การสำรวจเส้นทางที่กว้างที่สุดหรือไม่? 2. บทความ Wikipedia ที่คุณเชื่อมโยงไปนั้นมีความสับสนเล็กน้อย มันกล่าวถึงอัลกอริทึม Rabin-Shallit แต่ให้ตัวอย่างสำหรับวิธีที่แตกต่างอย่างสิ้นเชิง 3. มันน่าสนใจที่จะเห็นว่าเมื่อไรที่อัลกอริทึมของ Rabin-Shallit จะมีประสิทธิภาพสูงกว่าของคุณฉันคิดว่าการทดสอบแบบดั้งเดิมนั้นค่อนข้างแพงในทางปฏิบัติ
Dennis

1. ไม่ใช่หนึ่งเดียว 2. ที่นี่ฉันได้รับข้อมูลของฉัน (เช่นมีอัลกอริทึมนี้อยู่); ฉันไม่เห็นการวิเคราะห์หรือแม้แต่อ่านกระดาษ 3. เส้นโค้งจะสูงชันประมาณ 1e60 ว่ามันจะไม่สำคัญว่า 'ช้า' O (log²n)มันจะยังคงข้ามไปที่จุดนั้น
primo

1
ลิงค์ที่สองในคำถามอธิบายถึงวิธีการใช้ Rabin-Shallit แต่ไม่ได้พูดถึงความซับซ้อน คำตอบเกี่ยวกับ MathOverflowนี้ให้บทสรุปที่ดีของกระดาษ โดยวิธีการที่คุณค้นพบอัลกอริทึมที่ใช้โดย Gottfried Ruckle ในปี 1911 ( ลิงค์ )
เดนนิส

6

Python 3 (177)

N=int(input())
k=1
while N%4<1:N//=4;k*=2
n=int(N**.5)
R=range(int(2*n**.5)+1)
print([(a*k,b*k,c*k,d*k)for d in R for c in R for b in R for a in[n,n-1]if a*a+b*b+c*c+d*d==N][0])

หลังจากที่เราลดอินพุตNให้หารด้วย 4 ไม่ได้จะต้องแสดงให้เห็นว่าเป็นผลรวมของสี่เหลี่ยมสี่ช่องซึ่งหนึ่งในนั้นมีค่ามากที่สุดที่เป็นไปได้a=int(N**0.5)หรือน้อยกว่านั้นเหลือเหลือเพียงเล็กน้อยจากผลรวมของสามสี่เหลี่ยมจัตุรัสอื่น ๆ การดูแล. สิ่งนี้จะลดพื้นที่การค้นหาลงอย่างมาก

นี่เป็นข้อพิสูจน์ในภายหลังรหัสนี้จะหาทางแก้ปัญหาเสมอ เราต้องการที่จะหาสิ่งaที่n-a^2เป็นผลรวมของสามสี่เหลี่ยม จากช็สามสแควร์ทฤษฎีบท4^j(8*k+7)จำนวนคือผลรวมของสามสี่เหลี่ยมเว้นแต่เป็นรูปแบบ โดยเฉพาะอย่างยิ่งตัวเลขดังกล่าวเป็น 0 หรือ 3 (modulo 4)

เราแสดงให้เห็นว่าไม่มีผู้ใดติดต่อกันสองคนaสามารถทำให้จำนวนเงินที่เหลือN-a^2มีรูปร่างเช่นนี้สำหรับทั้งสองค่าติดต่อกันเราสามารถทำได้โดยการสร้างตารางaและNโมดูโล 4 โดยสังเกตว่าN%4!=0เพราะเราได้ดึงพลังทั้งหมด 4 จากNทั้งหมด

  a%4= 0123
      +----
     1|1010
N%4= 2|2121  <- (N-a*a)%4
     3|3232

เพราะไม่มีสองติดต่อกันaให้(N-a*a)%4 in [0,3]หนึ่งของพวกเขามีความปลอดภัยในการใช้งาน ดังนั้นเราละโมบใช้ที่ใหญ่ที่สุดที่เป็นไปได้nด้วยและn^2<=N n-1ตั้งแต่N<(n+1)^2ส่วนที่เหลือN-a^2จะได้รับการแสดงเป็นผลรวมของสามสี่เหลี่ยมที่มากที่สุดซึ่งเท่ากับ(n+1)^2 -(n-1)^2 4*nดังนั้นจึงพอเพียงที่จะตรวจสอบเฉพาะค่าสูงสุด2*sqrt(n)ซึ่งเป็นช่วงที่Rแน่นอนเท่านั้น

หนึ่งในการเพิ่มประสิทธิภาพต่อไปอาจเวลาการทำงานโดยการหยุดหลังจากที่การแก้ปัญหาเดียว, คอมพิวเตอร์มากกว่าการทำซ้ำสำหรับค่าที่ผ่านมาdและการค้นหาเพียง b<=c<=dแต่ในหมู่ค่า แต่ถึงแม้จะไม่มีการเพิ่มประสิทธิภาพเหล่านี้อินสแตนซ์ที่แย่ที่สุดที่ฉันสามารถค้นพบได้ใน 45 วินาทีในเครื่อง

ห่วงโซ่ของ "for x in R" โชคร้าย มันอาจจะสั้นลงโดยการแทนที่สตริงหรือเปลี่ยนโดยทำซ้ำมากกว่าดัชนีเดียวที่เข้ารหัส (a, b, c, d) เครื่องมือนำเข้า itertools เปิดออกไม่คุ้มค่า

แก้ไข: เปลี่ยนเป็นint(2*n**.5)+1จาก2*int(n**.5)+2เพื่อให้ตัวล้างอาร์กิวเมนต์นับตัวอักษรเดียวกัน


สิ่งนี้ไม่ได้ผลสำหรับฉัน ...5 => (2, 1, 0, 0)
Harry Beadle

แปลกมันใช้งานได้สำหรับฉัน: ฉัน5 => (2, 1, 0, 0)ใช้งานบน Ideone 3.2.3 หรือใน Idle 3.2.2 คุณได้อะไร
xnor

1
@xnor BritishColour 5 => (2, 1, 0, 0)ได้รับ คุณยังอ่านความคิดเห็นหรือไม่ (ตอนนี้เรามี 3 ความคิดเห็นในแถวที่มีข้อมูลรหัสนั้นเราสามารถให้แนวไปได้หรือไม่)
Justin

@Quincunx ถ้าเราจะถอดรหัสก็หมายความว่า5 => (2, 1, 0, 0) 2^2 + 1^2 + 0^2 + 0^2 = 5ใช่เราทำได้
ดร. Rebmu

1
Quincunx ฉันอ่านความคิดเห็นของ @ BritishColour และเท่าที่ฉันจะเห็นว่า5 => (2, 1, 0, 0)ถูกต้อง ตัวอย่างในคำถามให้พิจารณาว่า 0 ^ 2 = 0 เป็นหมายเลขสแควร์ที่ถูกต้อง ดังนั้นฉันจึงตีความ (อย่างที่ฉันคิดว่า xnor ทำ) ที่ British Color มีอย่างอื่น สีของอังกฤษที่คุณไม่ได้ตอบกลับมาอีกครั้งเราจะสมมติได้2,1,0,0ไหมว่าจริง ๆ แล้วคุณจะได้รับ?
เลเวลริเวอร์เซนต์

5

CJam , 91 90 74 71 ไบต์

q~{W):W;:N4md!}gmqi257:B_**_{;)_[Bmd\Bmd]_N\{_*-}/mq_i@+\1%}g{2W#*}%`\;

กะทัดรัด แต่ช้ากว่าวิธีอื่นของฉัน

ลองออนไลน์! วางรหัสพิมพ์จำนวนเต็มที่ต้องการในการป้อนข้อมูลและคลิกRun

พื้นหลัง

โพสต์นี้เริ่มต้นเป็น99 ไบต์คำตอบ ในขณะที่ยังมีพื้นที่สำหรับการปรับปรุง GolfScript ยังขาดฟังก์ชั่น sqrt ในตัว ฉันเก็บเวอร์ชันของ GolfScript ไว้จนกว่าจะมีการแก้ไข 5เนื่องจากคล้ายกับเวอร์ชัน CJam มาก

อย่างไรก็ตามการปรับให้เหมาะสมตั้งแต่การแก้ไขครั้งที่ 6 ต้องการตัวดำเนินการที่ไม่พร้อมใช้งานใน GolfScript ดังนั้นแทนที่จะโพสต์คำอธิบายแยกกันสำหรับทั้งสองภาษาฉันตัดสินใจที่จะวางเวอร์ชันการแข่งขันที่น้อยลง

อัลกอริทึมที่นำมาใช้คำนวณตัวเลขด้วยกำลังดุร้าย:

  1. สำหรับการป้อนข้อมูลmการค้นหาNและดังกล่าวว่าWm = 4**W * N

  2. i = 257**2 * floor(sqrt(N/4))ชุด

  3. i = i + 1ชุด

  4. พบ integers j, k, lดังกล่าวว่าที่i = 257**2 * j + 257 * k + lk, l < 257

  5. ตรวจสอบว่าd = N - j**2 - k**2 - l**2เป็นสี่เหลี่ยมจัตุรัสที่สมบูรณ์แบบหรือไม่

  6. หากไม่เป็นเช่นนั้นและกลับไปที่ขั้นตอนที่ 3

  7. 2**W * j, 2**W * k, 2**W * l, 2**W * sqrt(m)พิมพ์

ตัวอย่าง

$ TIME='\n%e s' time cjam lagrange.cjam <<< 999999999
[27385 103 15813 14]
0.46 s
$ TIME='\n%e s' time cjam lagrange.cjam <<< 805306368
[16384 16384 0 16384]
0.23 s

การกำหนดเวลาสอดคล้องกับ Intel Core i7-4700MQ

มันทำงานอย่างไร

q~              " Read and interpret the input. ";
{
  W):W;         " Increment “W” (initially -1). ";
  :N            " Save the integer on the stack in “N”. ';
  4md!          " Push N / 4 and !(N % 4). ";
}g              " If N / 4 is an integer, repeat the loop.
mqi             " Compute floor(sqrt(N/4)). ";
257:B_**        " Compute i = floor(sqrt(N)) * 257**2. ";
_               " Duplicate “i” (dummy value). ";
{               " ";
  ;)_           " Pop and discard. Increment “i”. ";
  [Bmd\Bmd]     " Push [ j k l ], where i = 257**2 * j + 257 * k + l and k, l < 257. ";
  _N\           " Push “N” and swap it with a copy of [ j k l ]. ";
  {_*-}/        " Compute m = N - j**2 - k**2 - l**2. ";
  mq            " Compute sqrt(m). ";
  _i            " Duplicate sqrt(m) and compute floor(sqrt(m)). ";
  @+\           " Form [ j k l floor(sqrt(m)) ] and swap it with sqrt(m). ";
  1%            " Check if sqrt(m) is an integer. ";
}g              " If it is, we have a solution; break the loop. ";
{2W#*}%         " Push 2**W * [ j k l sqrt(m) ]. ";
`\;             " Convert the array into a string and discard “i”. ";

2

C, 228

สิ่งนี้ขึ้นอยู่กับอัลกอริทึมในหน้า Wikipedia ซึ่งเป็น O (n) brute-force

n,*l,x,y,i;main(){scanf("%d",&n);l=calloc(n+1,8);
for(x=0;2*x*x<=n;x++)for(y=x;(i=x*x+y*y)<=n;y++)l[2*i]=x,l[2*i+1]=y;
for(x=0,y=n;;x++,y--)if(!x|l[2*x+1]&&l[2*y+1]){
printf("%d %d %d %d\n",l[2*x],l[2*x+1],l[2*y],l[2*y+1]);break;}}

2

GolfScript, 133 130 129 ไบต์

~{.[4*{4/..4%1$!|!}do])\}:r~,(2\?:f;{{..*}:^~4-1??n*,}:v~)..
{;;(.^3$\-r;)8%!}do-1...{;;;)..252/@252%^@^@+4$\-v^@-}do 5$]{f*}%-4>`

เร็ว แต่มีความยาว บรรทัดใหม่สามารถลบออกได้

ลองออนไลน์ โปรดทราบว่าล่ามออนไลน์มีการ จำกัด เวลา 5 วินาทีดังนั้นมันอาจไม่ทำงานกับหมายเลขทั้งหมด

พื้นหลัง

อัลกอริทึมใช้ประโยชน์จากทฤษฎีบทสามจตุรัสของเลอช็องดร์ซึ่งระบุว่าทุกจำนวนธรรมชาติnที่ไม่ได้อยู่ในรูปแบบ

                                                                   n = 4 ** a * (8b + 7)

สามารถแสดงเป็นผลรวมของสามกำลังสอง

อัลกอริทึมทำสิ่งต่อไปนี้:

  1. 4**i * jด่วนจำนวนเป็น

  2. ค้นหาจำนวนเต็มที่ใหญ่ที่สุดkเช่นนั้นk**2 <= jและj - k**2เป็นไปตามสมมติฐานของทฤษฎีบทสามตารางของเลอช็องดร์

  3. i = 0ชุด

  4. ตรวจสอบว่าj - k**2 - (i / 252)**2 - (i % 252)**2เป็นสี่เหลี่ยมจัตุรัสที่สมบูรณ์แบบหรือไม่

  5. ถ้าไม่ใช่ให้เพิ่มiและกลับไปที่ขั้นตอนที่ 4

ตัวอย่าง

$ TIME='%e s' time golfscript legendre.gs <<< 0
[0 0 0 0]
0.02 s
$ TIME='%e s' time golfscript legendre.gs <<< 123456789
[32 0 38 11111]
0.02 s
$ TIME='%e s' time golfscript legendre.gs <<< 999999999
[45 1 217 31622]
0.03 s
$ TIME='%e s' time golfscript legendre.gs <<< 805306368
[16384 0 16384 16384]
0.02 s

การกำหนดเวลาสอดคล้องกับ Intel Core i7-4700MQ

มันทำงานอย่างไร

~              # Interpret the input string. Result: “n”
{              #
  .            # Duplicate the topmost stack item.
  [            #
    4*         # Multiply it by four.
    {          #
      4/       # Divide by four.
      ..       # Duplicate twice.
      4%1$     # Compute the modulus and duplicate the number.
      !|!      # Push 1 if both are truthy.
    }do        # Repeat if the number is divisible by four and non-zero.
  ]            # Collect the pushed values (one per iteration) into an array.
  )\           # Pop the last element from the array and swap it with the array.
}:r~           # Save this code block as “r” and execute it.
,(2\?          # Get the length of the array, decrement it and exponentiate.
:f;            # Save the result in “f”.
               # The topmost item on the stack is now “j”, which is not divisible by 
               # four and satisfies that n = f**2 * j.
{              #
  {..*}:^~     # Save a code block to square a number in “^” and execute it.
  4-1??        # Raise the previous number to the power of 1/4.
               # The two previous lines compute (x**2)**(1/4), which is sqrt(abs(x)).
  n*,          # Repeat the string "\n" that many times and compute its length.
               # This casts to integer. (GolfScript doesn't officially support Rationals.)
}:v~           # Save the above code block in “v” and execute it.
)..            # Undo the first three instructions of the loop.
{              #
   ;;(         # Discard two items from the stack and decrement.
   .^3$\-      # Square and subtract from “n”.
   r;)8%!      # Check if the result satisfies the hypothesis of the three-square theorem.
}do            # If it doesn't, repeat the loop.
-1...          # Push 0 (“i”) and undo the first four instructions of the loop.
{              #
  ;;;)         # Discard two items from the stack and increment “i”.
  ..252/@252%  # Push the digits of “i” in base 252.
  ^@^@+4$\-    # Square both, add and subtract the result 
  v^@-         # Take square root, square and compare.
}do            # If the difference is a perfect square, break the loop.
5$]            # Duplicate the difference an collect the entire stack into an array.
{f*}%          # Multiply very element of the array by “f”.
-4>            # Reduce the array to its four last elements (the four numbers).
`              # Convert the result into a string.

1
j-k-(i/252)-(i%252)ผมไม่เข้าใจ จากความคิดเห็นของคุณ (ฉันลาดเทจริงอ่านรหัส) j-k-(i/252)^2-(i%252)^2ดูเหมือนว่าคุณหมายถึง BTW เทียบเท่ากับj-k-(i/r)^2-(i%r)^2ที่ r = sqrt (k) อาจบันทึกอักขระไม่กี่ตัว (และดูเหมือนว่าจะทำงานได้อย่างไม่มีปัญหาแม้สำหรับ k = 0 ในโปรแกรม C ของฉัน)
Level River St

@ สตีฟเวอร์ริลล์: ใช่ฉันทำผิดพลาด ขอบคุณสำหรับการสังเกต j-k^2-(i/252)^2-(i%252)^2มันควรจะเป็น ฉันยังคงรอ OP เพื่อชี้แจงว่า 0 เป็นอินพุตที่ถูกต้องหรือไม่ โปรแกรมของคุณจะช่วยให้สำหรับการป้อนข้อมูล1414 -nan 6 4.000000 0
Dennis

ฉันกำลังพูดถึงโปรแกรมใหม่ของฉันโดยใช้ทฤษฎีบทของเลอช็องดร์ซึ่งฉันยังไม่ได้โพสต์ ดูเหมือนว่าจะไม่เรียกรหัสด้วย% หรือ / เมื่อฉันมีค่าเทียบเท่า k = 0 ซึ่งเป็นสาเหตุที่ทำให้ไม่เกิดปัญหา คุณจะเห็นเมื่อฉันโพสต์ ดีใจที่คุณใช้โปรแกรมเดิมของฉันทำงาน คุณมีหน่วยความจำในการสร้างตาราง 2GB แบบเต็มใน rev 1 และใช้เวลานานเท่าใด
ระดับแม่น้ำเซนต์

ใช่คอมไพเลอร์ C สามารถทำงานได้ค่อนข้างคาดไม่ถึงเมื่อปรับให้เหมาะสม ใน GolfScript 0/=> ผิดพลาด! : P ฉันเรียกใช้ rev 1 ของคุณบนแล็ปท็อปของฉัน (i7-4700MQ, 8 GiB RAM) โดยเฉลี่ยเวลาดำเนินการคือ 18.5 วินาที
Dennis

ว้าวเป็นอย่างไร 18.5 วินาทีรวมถึงการสร้างตาราง? ใช้เวลาประมาณ 2 นาทีในเครื่องของฉัน ฉันเห็นปัญหาคือการจัดการหน่วยความจำของ Windows แทนที่จะให้โปรแกรมที่ต้องการ 2GB ทันทีให้เป็นชิ้นเล็ก ๆ ดังนั้นจึงต้องทำการสลับที่ไม่จำเป็นจำนวนมากจนกระทั่งจัดสรร 2GB เต็ม การค้นหาคำตอบต่อการป้อนข้อมูลของผู้ใช้จริงเร็วกว่ามากเพราะจากนั้นโปรแกรมไม่จำเป็นต้องไปขอความจำ
ระดับแม่น้ำเซนต์

1

Rev 1: C, 190

a,z,m;short s[15<<26];p(){m=s[a=z-a];printf("%d %f ",m,sqrt(a-m*m));}
main(){m=31727;for(a=m*m;--a;s[z<m*m?z:m*m]=a%m)z=a/m*(a/m)+a%m*(a%m);scanf("%d",&z);for(;a*!s[a]||!s[z-a];a++);p();p();}

นี่คือความทรงจำที่หิวยิ่งกว่า rev 0 หลักการเดียวกัน: สร้างตารางที่มีค่าบวกสำหรับผลบวกทั้งหมดที่เป็นไปได้ของ 2 สแควร์ส (และศูนย์สำหรับตัวเลขเหล่านั้นที่ไม่ใช่ผลรวมของสองสแควร์) จากนั้นค้นหา

ในการหมุนรอบนี้ใช้อาร์เรย์ของshortแทนcharการจัดเก็บ Hit ดังนั้นฉันสามารถเก็บรากของหนึ่งในสองของสี่เหลี่ยมในตารางแทนที่จะเป็นเพียงธง ฟังก์ชันนี้ช่วยให้ง่ายขึ้นp(สำหรับการถอดรหัสผลรวมของ 2 กำลังสอง) อย่างมากเนื่องจากไม่จำเป็นต้องวนซ้ำ

Windows มีขีด จำกัด 2GB สำหรับอาร์เรย์ ฉันจะได้รอบที่มีshort s[15<<26]อาร์เรย์ขององค์ประกอบ 1,666,32960 พอที่จะปฏิบัติตามสเป็ค แต่น่าเสียดายที่ขนาดรันไทม์โปรแกรมรวมยังคงมากกว่า 2GB และ (แม้จะมีการปรับเปลี่ยนการตั้งค่า OS) ผมยังไม่ได้รับสามารถที่จะทำงานได้มากกว่าขนาดนี้ (แม้ว่ามันจะเป็นไปได้ในทางทฤษฎี.) ดีที่สุดที่ฉันสามารถทำได้คือshort s[14<<26](939,524,096 องค์ประกอบ.) m*mจะต้องเป็น น้อยกว่านี้อย่างเคร่งครัด (30651 ^ 2 = 939483801) อย่างไรก็ตามโปรแกรมทำงานได้อย่างสมบูรณ์แบบและควรทำงานกับระบบปฏิบัติการใด ๆ ที่ไม่มีข้อ จำกัด นี้

รหัสที่ไม่ได้รับการปรับปรุง

a,z,m;
short s[15<<26];     
p(){m=s[a=z-a];printf("%d %f ",m,sqrt(a-m*m));}      
main(){       
 m=31727;             
 for(a=m*m;--a;s[z<m*m?z:m*m]=a%m)   //assignment to s[] moved inside for() is executed after the following statement. In this rev excessively large values are thrown away to s[m*m].
   z=a/m*(a/m)+a%m*(a%m);            //split a into high and low half, calculate h^2+l^2.                                  
 scanf("%d",&z); 
 for(;a*!s[a]||!s[z-a];a++);         //loop until s[a] and s[z-a] both contain an entry. s[0] requires special handling as s[0]==0, therefore a* is included to break out of the loop when a=0 and s[z] contains the sum of 2 squares.
 p();                                //print the squares for the first sum of 2 squares 
 p();}                               //print the squares for the 2nd sum of 2 squares (every time p() is called it does a=z-a so the two sums are exchanged.) 

Rev 0 C, 219

a,z,i,m;double t;char s[1<<30];p(){for(i=t=.1;(m=t)-t;i++)t=sqrt(a-i*i);printf("%d %f ",i-1,t);}
main(){m=1<<15;for(a=m*m;--a;){z=a/m*(a/m)+a%m*(a%m);s[z<m*m?z:0]=1;}scanf("%d",&z);for(;1-s[a]*s[z-a];a++);p();a=z-a;p();}

นี่คือสัตว์ร้ายหน่วยความจำหิว ใช้อาร์เรย์ 1GB คำนวณผลรวมทั้งหมดที่เป็นไปได้ของ 2 กำลังสองและเก็บค่าสถานะสำหรับแต่ละในอาร์เรย์ จากนั้นสำหรับการป้อนข้อมูลของผู้ใช้ z มันจะค้นหาอาร์เรย์สองผลรวมของ 2 กำลังสอง a และ za

ฟังก์ชั่นpแล้ว reconsitutes สี่เหลี่ยมเดิมที่ถูกนำมาใช้เพื่อให้ผลรวมของ 2 สี่เหลี่ยมaและz-aและพิมพ์พวกเขาครั้งแรกของแต่ละคู่เป็นจำนวนเต็มสองเป็นคู่ (ถ้ามีจะเป็นจำนวนเต็มทั้งหมดอีกสองตัวละครที่มีความจำเป็นt> m=t.)

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

รหัสที่ไม่ได้รับการดัดแปลง

a,z,i,m;
double t;
char s[1<<30];                              //handle numbers 0 up to 1073741823
p(){
 for(i=t=.1;(m=t)-t;i++)t=sqrt(a-i*i);      //where a contains the sum of 2 squares, search until the roots are found
 printf("%d %f ",i-1,t);}                   //and print them. m=t is used to evaluate the integer part of t. 

main(){       
 m=1<<15;                                   //max root we need is sqrt(1<<30);
 for(a=m*m;--a;)                            //loop m*m-1 down to 1, leave 0 in a
   {z=a/m*(a/m)+a%m*(a%m);s[z<m*m?z:0]=1;}  //split a into high and low half, calculate h^2+l^2. If under m*m, store flag, otherwise throw away flag to s[0]
 scanf("%d",&z);
 for(;1-s[a]*s[z-a];a++);                   //starting at a=0 (see above) loop until flags are found for sum of 2 squares of both (a) and (z-a)
 p();                                       //reconsitute and print the squares composing (a)
 a=z-a;                                     //assign (z-a) to a in order to...
 p();}                                      //reconsitute and print the squares composing (z-a)  

ตัวอย่างผลลัพธ์

แรกคือตามคำถาม ที่สองถูกเลือกให้เป็นสิ่งที่ยากต่อการค้นหา ในกรณีนี้โปรแกรมจะต้องค้นหาเท่าที่ 8192 ^ 2 + 8192 ^ 2 = 134217728 แต่ใช้เวลาเพียงไม่กี่วินาทีเมื่อสร้างตาราง

123456789
0 2.000000 3328 10601.000000

805306368
8192 8192.000000 8192 24576.000000

คุณไม่ควรเพิ่มต้นแบบสำหรับ sqrt ใช่ไหม
edc65

@ edc65 ฉันใช้ GCC คอมไพเลอร์ (ซึ่งใช้สำหรับ Linux แต่ฉันมีสภาพแวดล้อม Cygwin Linux ติดตั้งอยู่ในเครื่อง Windows ของฉัน) ซึ่งหมายความว่าฉันไม่จำเป็นต้องใส่#include <stdio.h>(สำหรับ scanf / printf) หรือ#include <math.h>(สำหรับ sqrt) เชื่อมโยงไลบรารีที่จำเป็นโดยอัตโนมัติ ฉันต้องขอบคุณเดนนิสสำหรับเรื่องนั้น (เขาบอกฉันในคำถามนี้ว่าcodegolf.stackexchange.com/a/26330/15599 ) เคล็ดลับการเล่นกอล์ฟที่ดีที่สุดที่ฉันเคยมี
ระดับแม่น้ำ St

ฉันสงสัยแล้วว่าทำไม Hunt the Wumpus จึงปรากฏในคำถามที่เชื่อมโยง :) โดยวิธีการที่ผมไม่ทราบว่าสิ่งที่ใช้ GCC บน Windows แต่ลิงเกอร์ GNU includeไม่เชื่อมโยงห้องสมุดคณิตศาสตร์โดยอัตโนมัติมีหรือไม่มี ในการรวบรวมบน Linux คุณต้องมีการตั้งค่าสถานะ-lm
Dennis

@Dennis ที่น่าสนใจก็ไม่ ได้แก่stdioห้องสมุดและอื่น ๆ อีกหลาย แต่ไม่ได้mathแม้กระทั่งกับinclude ? โดยที่ฉันเข้าใจถ้าคุณใส่ธงแปลคุณไม่ต้องการincludeหรือไม่ มันใช้งานได้ดีสำหรับฉันดังนั้นฉันจึงไม่บ่นขอขอบคุณอีกครั้งสำหรับคำแนะนำ BTW ฉันหวังว่าจะโพสต์คำตอบที่แตกต่างอย่างสิ้นเชิงโดยใช้ประโยชน์จากทฤษฎีบทของ Legendre (แต่มันจะยังคงใช้sqrtอยู่)
ระดับ River St

-lmส่งผลกระทบต่อ linker ไม่ใช่ตัวแปลภาษา gccopts ที่ไม่ต้องการต้นแบบสำหรับฟังก์ชั่นที่ "รู้" ดังนั้นมันจึงสามารถทำงานร่วมกับหรือไม่รวม อย่างไรก็ตามไฟล์ส่วนหัวให้เฉพาะฟังก์ชั่นต้นแบบไม่ใช่หน้าที่ตัวเอง บน Linux (ไม่ใช่ Windows) เห็นได้ชัดว่าlibmไลบรารีคณิตศาสตร์ไม่ใช่ส่วนหนึ่งของไลบรารีมาตรฐานดังนั้นคุณต้องแนะนำldให้เชื่อมโยงกับมัน
Dennis

1

Mathematica, 138 ตัวอักษร

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

S[n_]:=Module[{a,b,c,d},G=Floor@Sqrt@#&;a=G@n;b:=G[n-a^2];c:=G[n-a^2-b^2];d:=G[n-a^2-b^2-c^2];While[Total[{a,b,c,d}^2]!=n,a-=1];{a,b,c,d}]

หรือไม่มีเงื่อนไข:

S[n_] := Module[{a, b, c, d}, G = Floor@Sqrt@# &;
 a = G@n;
 b := G[n - a^2];
 c := G[n - a^2 - b^2];
 d := G[n - a^2 - b^2 - c^2];
 While[Total[{a, b, c, d}^2] != n, a -= 1];
 {a, b, c, d}
]

ฉันไม่ได้มองอัลกอริทึมมากเกินไป แต่ฉันคิดว่านี่เป็นความคิดเดียวกัน ฉันเพิ่งมากับวิธีแก้ปัญหาที่ชัดเจนและ tweaked จนกว่ามันจะทำงาน ฉันทดสอบมันสำหรับตัวเลขทั้งหมดระหว่าง 1 ถึงหนึ่งพันล้านและ ... ใช้ได้ การทดสอบใช้เวลาประมาณ 100 วินาทีบนเครื่องของฉัน

สิ่งที่ดีเกี่ยวกับเรื่องนี้คือเนื่องจาก b, c และ d ถูกกำหนดด้วยการมอบหมายที่ล่าช้า:=พวกเขาไม่จำเป็นต้องถูกนิยามใหม่เมื่อ a ลดค่าลง สิ่งนี้บันทึกสองสามบรรทัดพิเศษที่ฉันมีมาก่อน ฉันอาจเล่นกอล์ฟเพิ่มเติมและซ้อนส่วนที่ซ้ำซ้อน แต่นี่เป็นร่างแรก

Oh, และคุณใช้มันเป็นS@123456789และคุณสามารถทดสอบได้ด้วยหรือ{S@#, Total[(S@#)^2]} & @ 123456789 # == Total[(S@#)^2]&[123456789]การทดสอบอย่างละเอียดคือ

n=0;
AbsoluteTiming@ParallelDo[If[e != Total[(S@e)^2], n=e; Abort[]] &, {e, 1, 1000000000}]
n

ฉันใช้คำสั่งพิมพ์ [] มาก่อน แต่นั่นทำให้มันช้าลงมากแม้ว่ามันจะไม่เคยถูกเรียก ไปคิด


นี่สะอาดจริงๆ! ฉันประหลาดใจที่พอเพียงรับค่าทุกอย่าง แต่สิ่งแรกที่ใหญ่ที่สุดเท่าที่จะเป็นไปได้ สำหรับการเล่นกอล์ฟมันอาจสั้นกว่าที่จะบันทึกn - a^2 - b^2 - c^2เป็นตัวแปรและตรวจสอบว่าd^2เท่ากับ
xnor

2
มันใช้งานได้จริงเหรอ? มันแก้ปัญหาอะไรได้บ้างสำหรับอินพุต 805306368
edc65

S [805306368] = {- 28383, 536 I, 32 I, I} ฮะ. นั่นจะสร้าง 805306368 เมื่อคุณหาผลรวม แต่เห็นได้ชัดว่ามีปัญหากับอัลกอริทึมนี้ ฉันเดาว่าฉันจะต้องถอนสิ่งนี้ในตอนนี้ ขอบคุณที่ชี้ให้เห็นว่า ...
krs013

2
ตัวเลขที่ล้มเหลวทั้งหมดดูเหมือนจะถูกหารด้วยพลังอันยิ่งใหญ่ที่ 2 โดยเฉพาะพวกมันดูเหมือนจะอยู่ในรูปแบบa * 4^(2^k)ของk>=2การดึงพลังทั้งหมดของ 4 ออกมาดังนั้นaมันจึงไม่ใช่ตัวคูณของ 4 (แต่อาจเป็นได้) ยิ่งไปกว่านั้นแต่ละตัวaมีทั้ง 3 mod 4 หรือสองหมายเลขดังกล่าว ตัวที่เล็กที่สุดคือ 192
xnor

1

Haskell 123 + 3 = 126

main=getLine>>=print.f.read
f n=head[map(floor.sqrt)[a,b,c,d]|a<-r,b<-r,c<-r,d<-r,a+b+c+d==n]where r=[x^2|x<-[0..n],n>=x^2]

แรงเดรัจฉานง่าย ๆ เหนือสี่เหลี่ยมที่คำนวณล่วงหน้า

ต้องการ-Oตัวเลือกการคอมไพล์ (ฉันเพิ่ม 3 chars สำหรับสิ่งนี้) ใช้เวลาน้อยกว่า 1 นาทีสำหรับกรณีที่เลวร้ายที่สุด 999950883

ผ่านการทดสอบบน GHC เท่านั้น


1

C: 198 ตัวอักษร

ฉันอาจบีบมันลงไปเหลือเพียง 100 ตัวอักษร สิ่งที่ฉันชอบเกี่ยวกับวิธีแก้ปัญหานี้คือปริมาณขยะที่น้อยที่สุดเพียงธรรมดาสำหรับวงวนทำสิ่งที่วงสำหรับควรทำ (ซึ่งจะบ้า)

i,a,b,c,d;main(n){for(scanf("%d",&n);a*a+b*b-n?a|!b?a*a>n|a<b?(--a,b=1):b?++b:++a:(a=b=0,--n,++i):c*c+d*d-i?c|!d?c*c>i|c<d?(--c,d=1):d?++d:++c:(a=b=c=d=0,--n,++i):0;);printf("%d %d %d %d",a,b,c,d);}

และ prettified อย่างมาก:

#include <stdio.h>

int n, i, a, b, c, d;

int main() {
    for (
        scanf("%d", &n);
        a*a + b*b - n
            ? a | !b
                ? a*a > n | a < b
                    ? (--a, b = 1)
                    : b
                        ? ++b
                        : ++a
                : (a = b = 0, --n, ++i)
            : c*c + d*d - i
                ? c | !d
                    ? c*c > i | c < d
                        ? (--c, d = 1)
                        : d
                            ? ++d
                            : ++c
                    : (a = b = c = d = 0, --n, ++i)
                : 0;
    );
    printf("%d %d %d %d\n", a, b, c, d);
    return 0;
}

แก้ไข: มันไม่เร็วพอสำหรับอินพุตทั้งหมด แต่ฉันจะกลับมาพร้อมโซลูชันอื่น ฉันจะปล่อยให้การดำเนินการที่ไม่เป็นระเบียบนี้ยังคงเดิม


1

Rev B: C, 179

a,b,c,d,m=1,n,q,r;main(){for(scanf("%d",&n);n%4<1;n/=4)m*=2;
for(a=sqrt(n),a-=(3+n-a*a)%4/2;r=n-a*a-b*b-c*c,d=sqrt(r),d*d-r;c=q%256)b=++q>>8;
printf("%d %d %d %d",a*m,b*m,c*m,d*m);}

ขอบคุณ @Dennis สำหรับการปรับปรุง คำตอบที่เหลือด้านล่างไม่ได้รับการอัพเดตจาก rev A

Rev A: C, 195

a,b,c,d,n,m,q;double r=.1;main(){scanf("%d",&n);for(m=1;!(n%4);n/=4)m*=2;a=sqrt(n);a-=(3+n-a*a)%4/2;
for(;(d=r)-r;q++){b=q>>8;c=q%256;r=sqrt(n-a*a-b*b-c*c);}printf("%d %d %d %d ",a*m,b*m,c*m,d*m);}

เร็วกว่าคำตอบอื่น ๆ ของฉันและมีหน่วยความจำน้อยกว่ามาก!

นี้ใช้http://en.wikipedia.org/wiki/Legendre%27s_three-square_theorem หมายเลขที่ไม่ใช่ของแบบฟอร์มต่อไปนี้สามารถแสดงเป็นผลรวมของ 3 กำลังสอง (ฉันเรียกสิ่งนี้ว่าแบบฟอร์มต้องห้าม):

4^a*(8b+7), or equivalently 4^a*(8b-1)

หมายเหตุว่าทุกตารางเลขคี่ที่มีรูปแบบ(8b+1)และทุกแม้แต่ตารางตัวเลขเผิน ๆ 4bของแบบฟอร์ม 4^a*(odd square)==4^a*(8b+1)แต่หนังเรื่องนี้ความจริงที่ว่าทุกตารางแม้ตัวเลขของแบบฟอร์ม เป็นผล2^x-(any square number < 2^(x-1))ให้แปลกxจะเป็นของต้องห้ามในแบบฟอร์ม ดังนั้นตัวเลขเหล่านี้และทวีคูณของพวกเขาจึงเป็นกรณีที่ยากซึ่งเป็นเหตุผลว่าทำไมโปรแกรมจำนวนมากที่นี่จึงแบ่งพลังของ 4 เป็นขั้นตอนแรก

ตามที่ระบุไว้ในคำตอบของ @ xnor N-a*aไม่สามารถอยู่ในรูปแบบต้องห้ามสำหรับค่าต่อเนื่อง 2 รายการaคือ ด้านล่างฉันนำเสนอรูปแบบที่เรียบง่ายของตารางของเขา นอกเหนือไปจากความเป็นจริงที่ว่าหลังจากการหารด้วย 4 N%4ไม่สามารถเท่ากับ 0 ทราบว่ามีเพียง 2 (a*a)%4ค่าเป็นไปได้สำหรับ

(a*a)%4= 01
        +--
       1|10
  N%4= 2|21  <- (N-a*a)%4
       3|32

ดังนั้นเราจึงต้องการที่จะหลีกเลี่ยงค่าของ(N-a*a)ที่อาจอยู่ในรูปแบบที่ต้องห้ามคือผู้ที่(N-a*a)%4เป็น 3 หรือ 0. ที่สามารถเห็นนี้ไม่สามารถเกิดขึ้นสำหรับเดียวกันกับทั้งคู่และคี่N(a*a)

ดังนั้นอัลกอริทึมของฉันทำงานดังนี้:

1. Divide out powers of 4
2. Set a=int(sqrt(N)), the largest possible square
3. If (N-a*a)%4= 0 or 3, decrement a (only once)
4. Search for b and c such that N-a*a-b*b-c*c is a perfect square

ฉันชอบวิธีที่ฉันทำขั้นตอนที่ 3 โดยเฉพาะฉันเพิ่ม 3 ลงไปNดังนั้นจำเป็นต้องมีการลดลงถ้า(3+N-a*a)%4 =3 หรือ 2 (แต่ไม่ใช่ 1 หรือ 0) หารด้วย 2 และงานทั้งหมดสามารถทำได้โดยการแสดงออกที่ค่อนข้างง่าย .

รหัสที่ไม่ได้รับการปรับปรุง

จดบันทึกforลูปเดี่ยวqและใช้การหาร / โมดูโลเพื่อรับค่าของbและcจากมัน ฉันพยายามใช้aเป็นตัวหารแทน 256 เพื่อบันทึกไบต์ แต่บางครั้งค่าaไม่ถูกต้องและโปรแกรมหยุดทำงานอาจเป็นไปไม่สิ้นสุด 256 คือการประนีประนอมที่ดีที่สุดที่ฉันสามารถใช้>>8แทน/256การแบ่ง

a,b,c,d,n,m,q;double r=.1;
main(){
  scanf("%d",&n);
  for(m=1;!(n%4);n/=4)m*=2;
  a=sqrt(n);
  a-=(3+n-a*a)%4/2;
  for(;(d=r)-r;q++){b=q>>8;c=q%256;r=sqrt(n-a*a-b*b-c*c);}
  printf("%d %d %d %d ",a*m,b*m,c*m,d*m);}

เอาท์พุต

การเล่นโวหารที่น่าสนใจก็คือถ้าคุณป้อนหมายเลขตาราง, N-(a*a)= 0 แต่โปรแกรมตรวจพบว่า0%4= 0 และการลดลงของสี่เหลี่ยมจัตุรัสถัดไป อันเป็นผลมาจากตารางปัจจัยการผลิตจำนวนจะถูกย่อยสลายเสมอในกลุ่มของช่องสี่เหลี่ยมเล็ก ๆ 4^xจนกว่าพวกเขาจะอยู่ในรูปแบบ

999999999
31621 1 161 294

805306368
16384 0 16384 16384

999950883
31621 1 120 221

1
0 0 0 1

2
1 0 0 1

5
2 0 0 1

9
2 0 1 2

25
4 0 0 3

36
4 0 2 4

49
6 0 2 3

81
8 0 1 4

121
10 1 2 4

! ที่น่าตื่นตาตื่นใจ 0.003 วินาทีสำหรับทุกอินพุต! คุณจะได้รับผู้ 5 ตัวอักษรกลับ: 1. ประกาศก่อนm=1 main2. ดำเนินการscanfในforคำสั่ง 3. ใช้แทนfloat double4. จะสั้นกว่าn%4<1 !(n%4)5. มีช่องว่างที่ล้าสมัยในสตริงการจัดรูปแบบของ printf
Dennis


ขอบคุณสำหรับเคล็ดลับ! n-=a*aใช้งานไม่ได้เพราะaสามารถแก้ไขได้ในภายหลัง (มันให้คำตอบที่ผิดและค้างไว้ในบางกรณีเช่น 100 + 7 = 107) ฉันรวมที่เหลือทั้งหมด มันคงจะดีถ้ามีบางสิ่งที่สั้นลงprintfแต่ฉันคิดว่าคำตอบเดียวคือเปลี่ยนภาษา กุญแจสำคัญในการเร่งความเร็วคือการจับคู่กับความคุ้มค่าaได้อย่างรวดเร็ว เขียนใน C และด้วยพื้นที่การค้นหาน้อยกว่า 256 ^ 2 นี่อาจเป็นโปรแกรมที่เร็วที่สุดที่นี่
ระดับแม่น้ำเซนต์

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

0

JavaScript - 175 191 176 173 ตัวอักษร

กำลังดุร้าย แต่รวดเร็ว

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

แก้ไข 2 กำจัดฟังก์ชั่นเอาท์พุทภายในลูปแล้วบังคับให้ออกจากการแข่งขัน

แก้ไข 3 0 ไม่ใช่อินพุตที่ถูกต้อง

v=(p=prompt)();for(m=1;!(v%4);m+=m)v/=4;for(a=-~(q=Math.sqrt)(v);a--;)for(w=v-a*a,b=-~q(w);b--;)for(x=w-b*b,c=-~q(x);c--;)(d=q(x-c*c))==~~d&&p([m*a, m*b, m*c, m*d],a=b=c='')

Ungolfed:

v = prompt();

for (m = 1; ! (v % 4); m += m) 
{
  v /= 4;
}
for (a = - ~Math.sqrt(v); a--;) /* ~ force to negative integer, changing sign lead to original value + 1 */
{
  for ( w = v - a*a, b = - ~Math.sqrt(w); b--;)
  {
    for ( x = w - b*b, c = - ~Math.sqrt(x); c--;)
    {
      (d = Math.sqrt(x-c*c)) == ~~d && prompt([m*a, m*b, m*c, m*d], a=b=c='') /* 0s a,b,c to exit loop */
    }
  }
}

ตัวอย่างผลลัพธ์

123456789
11111,48,10,8

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