ฟังก์ชัน Ackermann


34

ฟังก์ชัน Ackermann นั้นมีความโดดเด่นในการเป็นหนึ่งในตัวอย่างที่ง่ายที่สุดของฟังก์ชันที่คำนวณได้ทั้งหมดซึ่งไม่ได้เรียกซ้ำแบบดั้งเดิม

เราจะใช้คำจำกัดความของA(m,n)การรับในจำนวนเต็มสองไม่ใช่ลบที่

A(0,n) = n+1
A(m,0) = A(m-1,1)
A(m,n) = A(m-1,A(m,n-1))

คุณอาจใช้

  • ฟังก์ชันที่ระบุชื่อหรือไม่ระบุชื่อโดยรับค่าจำนวนเต็มสองค่าเป็นอินพุตคืนค่าจำนวนเต็มหรือ
  • โปรแกรมที่ใช้จำนวนเต็มสองช่องว่างหรือจำนวนเต็มคั่นบรรทัดใหม่บน STDIN พิมพ์ผลลัพธ์ไปที่ STDOUT

คุณไม่สามารถใช้ฟังก์ชัน Ackermann หรือฟังก์ชั่น hyperexponentiation จากห้องสมุดหากมีอยู่ แต่คุณสามารถใช้ฟังก์ชันอื่นจากห้องสมุดอื่น ๆ อนุญาตให้ใช้การยกกำลังแบบปกติ

ฟังก์ชันของคุณจะต้องสามารถหาค่าของA(m,n)m of 3 และ n ≤ 10 ในเวลาน้อยกว่าหนึ่งนาที อย่างน้อยที่สุดมันจะต้องยุติตามทฤษฎีในอินพุตอื่น ๆ : เนื่องจากสแต็กสเปซที่ไม่มีที่สิ้นสุด, ประเภท Bigint ดั้งเดิม, และระยะเวลานานตามอำเภอใจ, มันจะกลับคำตอบ. แก้ไข:หากภาษาของคุณมีค่าความลึกการเรียกซ้ำเริ่มต้นที่ จำกัด เกินไปคุณอาจกำหนดค่าใหม่ได้โดยไม่ต้องเสียค่าตัวอักษร

การส่งที่มีจำนวนอักขระสั้นที่สุดจะชนะ

นี่คือค่าบางอย่างเพื่อตรวจสอบคำตอบของคุณ:

  A  | n=0     1     2     3     4     5     6     7     8     9    10
-----+-----------------------------------------------------------------
 m=0 |   1     2     3     4     5     6     7     8     9    10    11
   1 |   2     3     4     5     6     7     8     9    10    11    12
   2 |   3     5     7     9    11    13    15    17    19    21    23
   3 |   5    13    29    61   125   253   509  1021  2045  4093  8189
   4 |  13 65533   big   really big...

15
สิ่งนี้ไม่เคยถูกถามมาก่อนหรือไม่?
งานอดิเรกของ Calvin

9
ฉันคิดว่ามันคงจะสนุกมากถ้าจะทำโค้ดที่เร็วที่สุด
Sp3000

22
การลงคะแนนเนื่องจากไม่มีความท้าทายที่นี่ คำตอบที่ชัดเจน - เพียงแค่ใช้ฟังก์ชั่นอย่างไร้เดียงสาตามคำจำกัดความของมันเท่านั้น - จะเป็นคำตอบที่ดีที่สุดเสมอ ดังนั้นคำถามก็คือ "ภาษาใดที่มีจำนวนอักขระน้อยที่สุดในการแสดงออกที่ชัดเจนของฟังก์ชันของ Ackermann" ผู้ชนะที่แท้จริงคือภาษาการเขียนโปรแกรมไม่ใช่ผู้ที่เขียนโปรแกรมที่ชัดเจนในนั้น
David Richerby

1
จะเกิดอะไรขึ้นถ้าขีด จำกัด การเรียกซ้ำภาษาของฉันต่ำเกินไปที่จะคำนวณA(3,8)และสูงกว่าที่ไร้เดียงสาเหมือนที่คนอื่นทำ ฉันต้องหาวิธีแก้ปัญหาแบบไม่มีการเรียกซ้ำหรือไม่หรือฉันสามารถเพียงแค่ "สันนิษฐานว่าพื้นที่สแต็กอนันต์" ในกรณีเหล่านี้ได้หรือไม่? ฉันค่อนข้างแน่ใจว่าจะยุติภายในหนึ่งนาที
Martin Ender

5
@DavidRicherby "คำตอบที่ชัดเจน [... ] จะเป็นคำตอบที่ดีที่สุดเสมอ" สิ่งนี้ไม่เป็นความจริงในทุกภาษา ฉันรู้สึกสกปรกเล็กน้อยเมื่อมีเพียงตัวอย่างในภาษาบ้านของฉัน แต่มีหลายวิธีในการแสดง Ackermann และในบางภาษาคุณสามารถประหยัดได้โดยใช้ข้อเท็จจริงนั้น นี่คือความตั้งใจของฉันสำหรับความท้าทาย
algorithmshark

คำตอบ:


7

Pyth , 19

DaGHR?atG?aGtHH1GhH

กำหนดaซึ่งทำงานเป็นฟังก์ชัน Ackermann โปรดทราบว่านี่ต้องใช้ความลึกการเรียกซ้ำที่สูงกว่าตัวรวบรวม pyth อย่างเป็นทางการที่อนุญาตให้คำนวณจนถึงทุกวันนี้a 3 10ดังนั้นฉันจึงเพิ่มความลึกการเรียกซ้ำ นี่ไม่ใช่การเปลี่ยนภาษาเพียงเพื่อคอมไพเลอร์

ทดสอบ:

$ time pyth -c "DaGHR?atG?aGtHH1GhH           ;a 3 10"
8189

real    0m0.092s
user    0m0.088s
sys     0m0.000s

คำอธิบาย:

DaGH                     def a(G,H):
    R                    return
    ?          G                (if G:
     atG                              (a(G-1,
        ?    H                               (if H:
         aGtH                                      a(G,H-1)
              1                               else:1)
                hH               else:H+1)

โดยพื้นฐานแล้วมันเป็นเงื่อนไขแรกในค่าความจริงGว่าจะคืนค่าหรือส่งกลับ H + 1 หากเรียกซ้ำอาร์กิวเมนต์แรกจะเป็น G-1 เสมอและเป็นเงื่อนไขเกี่ยวกับค่าความจริงHว่าจะใช้a(G,H-1)เป็นอาร์กิวเมนต์ที่สองหรือใช้1เป็นอาร์กิวเมนต์ที่สอง


ในปัจจุบัน Pyth (ผมถือว่านี้ถูกเพิ่มเข้ามาหลังจากที่ท้าทายนี้) ฉันคิดว่าคุณสามารถเพิ่มเติมหรือเปลี่ยนแปลงน้อยDaGHRไปMและจะa g(คำสั่งของการขัดแย้งเพื่อ?การเปลี่ยนแปลงหรือไม่)
ลินน์

@Mauris ใช่คุณสามารถใช้Mแทนและใช่?คำสั่งโต้แย้งเปลี่ยนไป ตอนนี้เงื่อนไขเป็นจริงจริงเท็จ มันเป็นเรื่องจริงเงื่อนไขเท็จ
isaacg

@ ข้อเท็จจริงเกี่ยวกับประวัติศาสตร์ Lynn Fun Pyth: คำถามนี้ได้รับอิทธิพลจาก isaacg ในการเปลี่ยน (อย่างน้อย) สองสิ่งเกี่ยวกับ Pyth: ขีด จำกัด การเรียกซ้ำและการเปลี่ยนเป็นM !
FryAmTheEggman

23

Haskell, 35

0%n=1+n
m%n=iterate((m-1)%)1!!(n+1)

%ฟังก์ชั่นนี้กำหนดให้ผู้ประกอบการ

งานนี้โดยสังเกตเห็นว่าm%n(ซึ่งaเป็นฟังก์ชั่น Ackerman) ที่สำหรับเป็นศูนย์mคือ(m-1)%การประยุกต์ใช้ครั้งเพื่อn+1 1ตัวอย่างเช่น3%2มีการกำหนดตาม2%(3%1)ที่เป็นอยู่2%(2%(3%0))และนี่คือ2%(2%(2%1))


เลวร้ายเกินไปที่ฉันไม่สามารถใช้0%nแทนn+1เพราะความสำคัญ
Haskeller ภูมิใจ


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

12

GolfScript (30)

{1$1>{1\){1$(\A}*\;}{+)}if}:A;

การสาธิตออนไลน์

หากไม่มี1>(ซึ่งเป็นกรณีพิเศษA(1, n)) จะใช้เวลา 9 นาทีในการคำนวณA(3, 10)บนคอมพิวเตอร์ที่ฉันได้ทำการทดสอบ ด้วยกรณีพิเศษมันเร็วพอที่การสาธิตแบบออนไลน์ใช้เวลาน้อยกว่า 10 วินาที

โปรดทราบว่านี่ไม่ใช่การแปลคำจำกัดความไร้เดียงสา ความลึก recursion mมีขอบเขตโดย

การผ่า

{             # Function boilerplate
  1$          # Get a copy of m: stack holds m n m
  1>{         # (Optimisation): if m is greater than 1
    1         #   Take this as the value of A(m, -1)
    \){       #   Repeat n+1 times:
              #     Stack: m A(m, i-1)
      1$(\    #     Stack: m m-1 A(m, i-1)
      A       #     Stack: m A(m, i)
    }*
    \;        #   Lose that unwanted copy of m
  }{          # Else
    +)        #   A(m in {0, 1}, n) = m + n + 1
  }if
}:A;          # Function boilerplate

ใน CJam 1>คุณจะไม่จำเป็นต้อง หลังจากลบ (และเปลี่ยนifเป็น?) การประมวลผล3 10 Aใช้เวลา 110 วินาทีกับล่ามออนไลน์และหกวินาทีด้วยล่าม Java
เดนนิส

7

แคลคูลัสแลมบ์ดาไบนารี , 54 บิต = 6.75 ไบต์

hexdump:

00000000: 1607 2d88 072f 68                        ..-../h

เลขฐานสอง:

000101100000011100101101100010000000011100101111011010

นี่คือλ เมตร ม.กรัม . λ n . กรัม ( n กรัม 1)) (λ n . λ . λ x . ( n x )) ซึ่งตัวเลขทั้งหมดจะแสดงเป็นตัวเลขคริสตจักร


6

JavaScript, ES6, 41 34 ไบต์

f=(m,n)=>m?f(m-1,!n||f(m,n-1)):n+1

ทำงานนี้ในคอนโซล Firefox รุ่นล่าสุดและมันจะสร้างฟังก์ชั่นที่เรียกว่าfที่คุณสามารถโทรหาที่มีค่าที่แตกต่างกันmและnเหมือน

f(3,2) // returns 29

หรือ

ลองรหัสด้านล่างใน Firefox ล่าสุด

f=(m,n)=>m?f(m-1,!n||f(m,n-1)):n+1

B.onclick=_=>alert(f(+M.value, +N.value))
#M,#N{max-width:15px;border: 1px solid;border-width:0 0 1px 0}
<div>f(<input id=M />,<input id=N />)</div><br><button id=B>Evaluate</button>


การทดสอบสิ่งนี้ด้วย 0,1 ใน Chrome ไม่ได้ผลลัพธ์
Nzall

3
ก่อนหน้านี้จะทำงานเฉพาะใน Firefox ล่าสุดเนื่องจาก ES6
เครื่องมือเพิ่มประสิทธิภาพ

ว้าว ... เรามีโซลูชัน JS ที่เหมือนกัน 4 วิธีทั้งหมดที่ 34 ไบต์ ฉันไม่เคยเห็นแบบนั้นมาก่อน
ETHproductions

6

Python 2.7.8 - 80, 54, 48, 46 45

A=lambda m,n:m and A(m-1,n<1or A(m,n-1))or-~n

(เครดิตถึง xnor!)

อ่านง่ายขึ้น แต่มีอีก 1 ตัวอักษร:

A=lambda m,n:n+(m<1or A(m-1,n<1or A(m,n-1))-n)

ไม่ว่าฉันจะต้องตั้งค่าในการสั่งซื้อเพื่อให้ได้ผลสำหรับsys.setrecursionlimit(10000) A(3,10)การเล่นกอล์ฟต่อไปโดยใช้การทำดัชนีแบบลอจิคัลไม่ทำงานเนื่องจากความลึกการวนซ้ำที่เพิ่มขึ้นอย่างมาก


1elseฉันได้รับข้อผิดพลาดทางไวยากรณ์ใน ตัวอักษรเริ่มต้นที่eทำให้เกิดปัญหาสำหรับ parser 1e3เพราะตัวเลขที่สามารถเขียนได้เช่น
xnor

บันทึกไม่กี่ตัวอักษรเปลี่ยนเป็นand/or:A=lambda m,n:m and A(m-1,n<1or A(m,n-1))or-~n
xnor

@xnor: ขอบคุณสำหรับเคล็ดลับ! ดูการสนทนานี้สำหรับปัญหาการแยกวิเคราะห์ Python 2.7.8 ยอมรับ1elseแต่เวอร์ชันอื่นส่วนใหญ่ไม่รองรับ
Falko

ขอบคุณสำหรับตัวชี้เกี่ยวกับ1else; มันช่วยให้ฉันบีบถ่านที่นี่และที่อื่น ๆ แต่ยี้คือรุ่นเฉพาะ! Python 2.7.4 ไม่อนุญาต คุณใช้เวอร์ชั่นออนไลน์ด้วย 2.7.8 หรือฉันจะต้องดาวน์โหลดหรือไม่
xnor

@xnor: เป็นการติดตั้งออฟไลน์ ideone.comเช่นล้มเหลวในการแยกวิเคราะห์1elseเช่นกัน
Falko

6

J - 26 ถ่าน

($:^:(<:@[`]`1:)^:(0<[)>:)

มีคำจำกัดความทางเลือกที่ทำงานได้มากขึ้นของ Ackermann:

Ack 0 n = n+1
Ack m n = Iter (Ack (m-1)) n
Iter f 0 = f 1
Iter f n = f (Iter f (n-1))

มันเกิดขึ้นที่Iterเขียนได้ง่ายมากใน J เพราะ J มีวิธีการส่งผ่านm-1ไปAckยังและเพื่อกำหนดค่าเริ่มต้นของIterการเป็น 1 อธิบายโดยการระเบิด:

(                      >:)  NB. increment n
                ^:(0<[)     NB. if m=0, do nothing to n+1; else:
   ^:                       NB. iterate...
($:                      )  NB.   self ($: is recursion)
     (<:@[     )            NB.   with left arg m-1
          `]                NB.   n+1 times
            `1:             NB.   starting on 1

สิ่งนี้ขึ้นอยู่กับสิ่งที่ J เรียกว่ารูปแบบ gerund ของ - โดย^:ปกติแล้ววิธีที่จะควบคุมขอบเขตทั้งหมดได้มากขึ้นในรูปแบบโดยปริยาย (ปราศจากจุด)

ที่ REPL:

   3 ($:^:(<:@[`]`1:)^:(0<[)>:) 3
61
   ack =: ($:^:(<:@[`]`1:)^:(0<[)>:)
   (i.4) ack"0 table (i.11)
+-----+------------------------------------------+
|ack"0|0  1  2  3   4   5   6    7    8    9   10|
+-----+------------------------------------------+
|0    |1  2  3  4   5   6   7    8    9   10   11|
|1    |2  3  4  5   6   7   8    9   10   11   12|
|2    |3  5  7  9  11  13  15   17   19   21   23|
|3    |5 13 29 61 125 253 509 1021 2045 4093 8189|
+-----+------------------------------------------+
   6!:2 '3 ($:^:(<:@[`]`1:)^:(0<[)>:) 10'  NB. snugly fits in a minute
58.5831

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


แก้ไข: 24 ถ่าน?

หลายปีต่อมาฉันพบวิธีแก้ปัญหาซึ่งสั้นกว่าตัวละครสองตัว

(0&<~(<:@#~$:/@,1:^:)>:)

มันช้ากว่ามาก: 3 ack 8ใช้เวลานานกว่าหนึ่งนาทีบนเครื่องของฉัน นี่เป็นเพราะ (1) ฉันใช้การพับ/แทนการวนซ้ำดังนั้น J อาจต้องจดจำสิ่งต่าง ๆ มากกว่าปกติและ (2) ในขณะ0&<~ที่ทำการคำนวณแบบเดียวกันกับที่(0<[)จริงมันจะได้รับการดำเนินการn+1ครั้งก่อนที่จะทำขั้นตอนซ้ำเมื่อเรียกใช้m ack n- 0&<เกิดขึ้น เป็น idempotent ดังนั้นมันจึงไม่ทำลายการคำนวณ แต่nจะใหญ่ขึ้นอย่างรวดเร็วและackซ้ำซาก

ฉันสงสัยว่าเครื่องที่ทรงพลังยิ่งกว่าสามารถกดรหัสใหม่ได้ในเวลาไม่กี่นาทีเพราะนี่เป็นคอมพิวเตอร์ที่รหัสเก่าสามารถค้นหาได้3 ack 10ในเวลาน้อยกว่า 15 วินาที


5

C - 41 ไบต์

ไม่มีสิ่งใดเลย - ขีด จำกัด เล็ก ๆ หมายความว่าค่าที่ต้องการทั้งหมดสามารถคำนวณได้ในเวลาน้อยกว่า 1 วินาทีโดยการทำตามคำจำกัดความของฟังก์ชันอย่างไร้เดียงสา

A(m,n){return!m?n+1:A(m-1,n?A(m,n-1):1);}


int main()
{
    int m,n;
    for(m = 0; m <= 3; m++)
    for(n = 0; n <= 10; n++)
    printf("%d %d %d\n", m,n,A(m,n));
    return 0;
}

5

Javascript ES6 (34)

a=(m,n)=>m?a(m-1,n?a(m,n-1):1):n+1

การดำเนินงาน:

a=(m,n)=>m?a(m-1,n?a(m,n-1):1):n+1
td[colspan="2"] input{width: 100%;}
<table><tbody><tr><td>m=</td><td><input id="m" type="number" value="0" /></td></tr><tr><td>n=</td><td><input id="n" type="number" value="0" /></td></tr><tr><td colspan="2"><input type="button" value="Calculate!" onclick="document.getElementById('out').value=a(document.getElementById('m').value, document.getElementById('n').value)" /></td></tr><tr><td colspan="2"><input id="out" disabled="disabled" type="text" /></td></tr></tbody></table>


4

JavaScript (ES6) - 34

A=(m,n)=>m?A(m-1,!n||A(m,n-1)):n+1

และทดสอบ:

> A=(m,n)=>m?A(m-1,!n||A(m,n-1)):n+1;s=new Date().getTime();console.log(A(3,10),(new Date().getTime() - s)/1000)
8189 16.441

3

Coq, 40

nat_rec _ S(fun _ b n=>nat_iter(S n)b 1)

nat -> nat -> natนี่คือการทำงานของประเภท เนื่องจาก Coq อนุญาตให้สร้างฟังก์ชั่นทั้งหมดเท่านั้นมันจึงเป็นหลักฐานอย่างเป็นทางการว่าการเกิดขึ้นของ Ackermann นั้นมีมายาวนาน

การสาธิต:

Welcome to Coq 8.4pl6 (November 2015)

Coq < Compute nat_rec _ S(fun _ b n=>nat_iter(S n)b 1) 3 10.
     = 8189
     : nat

หมายเหตุ: Coq 8.5 หลังจากปล่อยความท้าทายนี้เปลี่ยนไปnat_iterNat.iter



2

Mathematica ขนาด 46 ไบต์

0~a~n_:=n+1
m_~a~n_:=a[m-1,If[n<1,1,a[m,n-1]]]

a[3,10]ใช้เวลาสวยมากว่านาทีสำหรับ โปรดทราบว่าขีด จำกัด การเรียกซ้ำเริ่มต้นของ Mathematica นั้นเล็กเกินไปสำหรับa[3,8]และเกิน (อย่างน้อยในเครื่องของฉัน) แต่สามารถแก้ไขได้โดยการกำหนดค่า

$RecursionLimit = Infinity

1
ว้าวคุณจะบอกว่า JS เร็วกว่า Mathematica มากกว่า 25 เท่า?
เครื่องมือเพิ่มประสิทธิภาพ

@Optimizer อย่างน้อยที่สุดเมื่อพูดถึงการเรียกซ้ำ ... ฉันคิดว่าส่วนหนึ่งถ้ามันจะต้องคิดออกทุกครั้งที่คำจำกัดความที่ใช้และIfฟังก์ชั่นนั้นช้าลง
Martin Ender

1
ด้วยการบันทึกช่วยจำใช้เวลา 0.07 วินาที คือm_~a~n_:=m~a~n=...
Mark Adler

@ MarkAdler นั่นเป็นวิธีที่ดีมาก ๆ ในการทำ memoisation ใน Mathematica!
Martin Ender

2

Javascript กับ lambdas, 34

A=(m,n)=>m?A(m-1,n?A(m,n-1):1):n+1

คำตอบที่มีเล่ห์เหลี่ยมไม่สามารถทำให้สั้นลงได้


2

Haskell, 48 44 ตัวอักษร (36 สำหรับรายการ)

ในขณะที่ไม่สั้นเท่ากับโซลูชัน Haskell อื่น ๆ อันนี้น่าทึ่งเพราะมันแสดงฟังก์ชั่น Ackermann เป็นรายการที่ไม่มีที่สิ้นสุดซึ่งฉันคิดว่าเป็นระเบียบเรียบร้อย ผลที่ได้คือรายการที่ไม่มีที่สิ้นสุด (จากรายการที่ไม่มีที่สิ้นสุด) เช่นที่ที่ตำแหน่ง[m, n]มันถือค่าA (m, n)

รายการไม่มีที่สิ้นสุดของตัวเอง:

iterate(tail.(`iterate`1).(!!))[1..]

เป็นฟังก์ชั่น (เพื่อให้สอดคล้องกับข้อกำหนด):

i=iterate;m%n=i(tail.(`i`1).(!!))[1..]!!m!!n

สูตรที่ได้รับมาจากการสังเกตว่ากรณีทั่วไป / ทั่วไปสำหรับฟังก์ชั่น Ackermann คือการใช้ค่าทางด้านซ้ายเป็นดัชนีในแถวด้านบน กรณีพื้นฐานสำหรับการสอบถามซ้ำ (เช่นคอลัมน์ซ้ายสุดของแถวคือA (m, 0) ) คือการใช้ค่าซ้ายสุดที่สองในแถวด้านบน กรณีฐานที่เรียกซ้ำเป็นA (0, n) = 1 + n[1..]กรณีคือแถวแรกคือ

ดังนั้นเราจึงได้รับ

let a0 = [1..]
let a1 = tail $ iterate (a0 !!) 1  -- 'tail' because iterate starts by applying
let a2 = tail $ iterate (a1 !!) 1  -- the function 0 times
-- etc

จากนั้นเราก็เพิ่มอีกระดับของการทำซ้ำตามรูปแบบนั้นและทำการเล่นกลอย่างไม่มีจุดหมาย


คุณสามารถใช้นามแฝงiterateเป็นชื่อตัวอักษรเดียวเช่นi=iterate;ack=i ...
ภูมิใจ haskeller

@proudhaskeller โอ้ใช่ไม่ได้คิดอย่างนั้น ขอบคุณ! ยืมชื่อผู้ประกอบการของคุณใช้เช่นกัน
FireFly

2

Tiny Lisp , 70 (ออกจากการแข่งขัน)

สิ่งนี้หมดการแข่งขันเนื่องจากภาษาใหม่กว่าคำถามและไม่ประสบความสำเร็จในการรัน(A 3 10)ตามที่ต้องการในคำถามเนื่องจากมีการล้นสแต็ค

(d A(q((m n)(i m(i n(A(s m 1)(A m(s n 1)))(A(s m 1)1))(s n(s 0 1))))))

นี่เป็นการกำหนดฟังก์ชันAที่คำนวณฟังก์ชัน Ackermann จัดรูปแบบ:

(d A
   (q( (m n)
       (i m
          (i n
             (A (s m 1)
                (A m
                   (s n 1)
                 )
              ) 
             (A (s m 1)
                1
              )
           )
          (s n
             (s 0 1)
           )
        )
    ) )
 )

เราใช้แมโคร builtin ทั้งหมด ( d(define) และq(quote) และi(if)) และฟังก์ชัน builtin หนึ่งตัว ( s- ลบ) ที่นี่

i ดำเนินการส่วนที่แท้จริงของมันเมื่อเงื่อนไขเป็นตัวเลข> 0 (และส่วนที่ผิด) ดังนั้นเราจึงไม่จำเป็นต้องทำการเปรียบเทียบที่ชัดเจนที่นี่

sเป็นงานเฉพาะทางคณิตศาสตร์ที่มีอยู่ให้เราใช้มันสำหรับn-1/ m-1ตลอดจนเป็นสำหรับ(s n (s 0 1))n+1

Tiny lisp ใช้การปรับให้เหมาะสมแบบเรียกซ้ำหาง (tail recursion) แต่วิธีนี้จะช่วยในการAโทรออกนอกเท่านั้นในผลลัพธ์ไม่ใช่สำหรับการA(m, n-1)โทรที่ใช้สำหรับพารามิเตอร์

ด้วยการใช้งานกระเพื่อมเล็ก ๆ ของฉันใน Ceylon บน JVM มันใช้งานได้(A 3 5) = 253แต่ดูเหมือนจะพังทลายลงเมื่อพยายามคำนวณ(A 2 125)โดยตรง (ซึ่งควรให้ผลลัพธ์เดียวกัน) หากคำนวณว่าหลังจาก(A 3 4) = 125นั้น JVM ดูเหมือนว่าจะต้องปรับฟังก์ชั่นให้เหมาะสมพอที่จะอินไลน์การเรียกฟังก์ชั่นกลางบางอย่างในล่ามของฉันซึ่งจะช่วยให้ความลึกของการเรียกซ้ำเพิ่มขึ้น แปลก.

ดำเนินการอ้างอิงได้รับการขึ้นไป(A 3 5) = 253และยัง(A 2 163) = 329แต่ไม่ประสบความสำเร็จและดังนั้นจึงแม้แต่น้อย(A 2 164)(A 3 6) = (A 2 253)


นี้อาจจะมีการแข่งขันประหยัดสำหรับช่องว่างและวงเล็บ;)
แมว

2

ไป, 260 243 240 122 ไบต์

ฉันไม่เห็นว่าคำถามนี้อนุญาตให้ทำงานได้ไม่นาน

ไกลจากการแข่งขัน แต่ฉันกำลังเรียนภาษานี้และฉันต้องการทดสอบ

func (m,n int)int{r:=0
switch{case m==0&&n!=0:r=n+1
case m!=0&&n==0:r=a(m-1,1)
case m!=0&&n!=0:r=a(m-1,a(m,n-1))}
return r}

ใช้มันเหมือนgo run ack.goแล้วจัดหาตัวเลขสองและm nถ้า m> 4 หรือ n> 30 เวลาดำเนินการอาจเกินครึ่งนาที

สำหรับm=3 n=11:

$ time go run ack
16381
real    0m1.434s
user    0m1.432s
sys     0m0.004s

แก้ไข : บันทึกทั้งหมด 17 ไบต์โดยสลับไปที่switchover if/elseand dot-import


1
คุณสามารถทำได้ดียิ่งขึ้น! คำแถลงswitch 0 {case m:r=n+1 case n:r=a(m-1,1) default:r=a(m-1,a(m,n-1))}ของ Go switchนั้นช่างยืดหยุ่นอย่างมาก!
EMBLEM

@EMBLEM ขอบคุณมันนานมากแล้วที่ฉันได้เขียนบรรทัด Go แต่ฉันดีใจที่เห็นว่ามีนักกอล์ฟ Go-golf คนอื่น ๆ เกี่ยวกับ: D
cat

1

Haskell: 81 69 ไบต์

a::Int->Int->Int
a 0 n=n+1
a m 0=a (m-1) 1
a m n=a (m-1) a m (n-1)

a 3 10 ใช้เวลาประมาณ 45 วินาที


1
นี่คือรหัสกอล์ฟดังนั้นคุณควรพยายามทำให้รหัสสั้นที่สุดเท่าที่จะเป็นไปได้ ตัวอย่างเช่นลบช่องว่างที่ไม่จำเป็นและประเภทที่ชัดเจนออกมา
ภูมิใจ haskeller

คุณยังขาดความสำคัญในบรรทัดที่สี่ด้วย
ภูมิใจ Haskeller



1

Julia, 34 31 28 bytes

m\n=m>0?~-m\(n<1||m\~-n):n+1

นี่คือฟังก์ชั่นที่ไม่ระบุชื่อ มันเป็นการดำเนินการตรงไปตรงมาของคำนิยาม recursive เหยียดหยามความสามารถของจูเลียเพื่อผู้ประกอบการสร้างนิยามใหม่

ลองออนไลน์!


1

R - 54 52

ฉันใช้สิ่งนี้เป็นข้ออ้างในการลองและไปรอบ ๆ R ดังนั้นมันอาจจะแย่มาก :)

a=function(m,n)"if"(m,a(m-1,"if"(n,a(m,n-1),1)),n+1)

ตัวอย่างการวิ่ง

> a(3,8)
[1] 2045

ฉันได้รับสแต็คล้นสำหรับทุกสิ่ง

T-SQL- 222

ฉันคิดว่าฉันจะพยายามให้ T-SQL ทำเช่นกัน ใช้วิธีการอื่นเนื่องจากการเรียกซ้ำไม่ค่อยดีใน ​​SQL อะไรที่ระเบิดได้กว่า 4,2 ลูก

DECLARE @m INT=4,@n INT=1;WITH R AS(SELECT 2 C, 1 X UNION ALL   SELECT POWER(2,C),X+1FROM R)SELECT IIF(@m=0,@n+1,IIF(@m=1,@n+2,IIF(@m=2,2*@n+3,IIF(@m=3,POWER(2,@n+3)-3,IIF(@m=4,(SELECT TOP(1)C FROM R WHERE x= @n+3)-3,-1)))))

สำหรับ R submisson ของคุณดูเหมือนว่าคุณไม่ต้องการ{}แม้ว่าจะไม่มีการ จำกัด จำนวนสแต็กล้นเนื่องจาก R ไม่มี TCO ...
Giuseppe

@Giuseppe ขอบคุณ ... ในการป้องกันของฉันฉันยังใหม่กับมันแล้ว :)
MickyT

1

brainfuck , 90 ไบต์

>>>>+>,>,<<[>[>[-[->>>+<<<]<[->+>>+<<<]>-[-<+>]>+>>>>>]<[->+>>]]<[>>+[-<<<+>>>]<<-]<<<]>>.

ลองออนไลน์!

ถือว่าการใช้งานด้วยขนาดเซลล์ที่กำหนดเองโดยมี IO เป็นตัวเลข -6 ไบต์ถ้าคุณไม่สนใจใช้เซลล์เชิงลบ

เสร็จสิ้นในเวลาประมาณ 30 วินาทีเป็นเวลา 3,8 ในล่ามที่เชื่อมโยงให้คุณทำเครื่องหมายการตั้งค่าที่ถูกต้อง พิมพ์ตัวเลขป้อนใช้ได้กับ\s เช่นถูก3,9\3\9


1

Tcl , 67 ไบต์

proc tcl::mathfunc::A m\ n {expr {$m?A($m-1,$n?A($m,$n-1):1):$n+1}}

ลองออนไลน์!


Tcl 77 ไบต์

proc A m\ n {expr {$m?[A [expr $m-1] [expr {$n?[A $m [expr $n-1]]:1}]]:$n+1}}

ลองออนไลน์!

ในคอมไพเลอร์ออนไลน์มันล้มเหลวในการทำงานเนื่องจากหมดเวลา แต่ในล่าม Tcl ท้องถิ่นมันทำงานได้ดี ฉันทำโปรไฟล์ของการเรียกรูทแต่ละครั้งเพื่อAฟังก์ชั่นเพื่อดูว่าการคำนวณใช้เวลาเท่าใดสำหรับแต่ละคู่{m,n}ที่จะทดสอบ:

m=0, n=0, A=1, time=3.5e-5 seconds
m=0, n=1, A=2, time=2e-6 seconds
m=0, n=2, A=3, time=8e-6 seconds
m=0, n=3, A=4, time=1e-6 seconds
m=0, n=4, A=5, time=2e-6 seconds
m=0, n=5, A=6, time=1e-6 seconds
m=0, n=6, A=7, time=1e-6 seconds
m=0, n=7, A=8, time=1e-6 seconds
m=0, n=8, A=9, time=1e-6 seconds
m=0, n=9, A=10, time=0.0 seconds
m=0, n=10, A=11, time=1e-6 seconds
m=1, n=0, A=2, time=4e-6 seconds
m=1, n=1, A=3, time=6e-6 seconds
m=1, n=2, A=4, time=1e-5 seconds
m=1, n=3, A=5, time=1.2e-5 seconds
m=1, n=4, A=6, time=1.5e-5 seconds
m=1, n=5, A=7, time=2e-5 seconds
m=1, n=6, A=8, time=2e-5 seconds
m=1, n=7, A=9, time=2.6e-5 seconds
m=1, n=8, A=10, time=3e-5 seconds
m=1, n=9, A=11, time=3e-5 seconds
m=1, n=10, A=12, time=3.3e-5 seconds
m=2, n=0, A=3, time=8e-6 seconds
m=2, n=1, A=5, time=2.2e-5 seconds
m=2, n=2, A=7, time=3.9e-5 seconds
m=2, n=3, A=9, time=6.3e-5 seconds
m=2, n=4, A=11, time=9.1e-5 seconds
m=2, n=5, A=13, time=0.000124 seconds
m=2, n=6, A=15, time=0.000163 seconds
m=2, n=7, A=17, time=0.000213 seconds
m=2, n=8, A=19, time=0.000262 seconds
m=2, n=9, A=21, time=0.000316 seconds
m=2, n=10, A=23, time=0.000377 seconds
m=3, n=0, A=5, time=2.2e-5 seconds
m=3, n=1, A=13, time=0.000145 seconds
m=3, n=2, A=29, time=0.000745 seconds
m=3, n=3, A=61, time=0.003345 seconds
m=3, n=4, A=125, time=0.015048 seconds
m=3, n=5, A=253, time=0.059836 seconds
m=3, n=6, A=509, time=0.241431 seconds
m=3, n=7, A=1021, time=0.971836 seconds
m=3, n=8, A=2045, time=3.908884 seconds
m=3, n=9, A=4093, time=15.926341 seconds
m=3, n=10, A=8189, time=63.734713 seconds

มันล้มเหลวในคู่สุดท้าย{m,n}={3,10}เนื่องจากใช้เวลาน้อยกว่าหนึ่งนาที

สำหรับค่าที่สูงขึ้นของmมันจะต้องเพิ่มrecursionlimitค่า


ฉันให้สั้นลงถึง 65 ไบต์ แต่จะไม่เป็นไปตามข้อกำหนดของคำถาม "ฟังก์ชั่นของคุณจะต้องสามารถหาค่า A (m, n) สำหรับ m ≤ 3 และ n ≤ 10 ในเวลาน้อยกว่าหนึ่งนาที" หากไม่มี{}ก็จะหมดเวลาใน TIO และจะไม่ทำการสาธิตของสองรายการสุดท้าย

Tcl , 65 ไบต์

proc tcl::mathfunc::A m\ n {expr $m?A($m-1,$n?A($m,$n-1):1):$n+1}

ลองออนไลน์!


0

J: 50

>:@]`(1$:~<:@[)`(<:@[$:[$:_1+])@.(0>.[:<:@#.,&*)M.

ส่งคืนในเสี้ยววินาทีสำหรับ 0 ... 3 กับ 0 ... 10:

   A=:>:@]`(1$:~<:@[)`(<:@[$:[$:_1+])@.(0>.[:<:@#.,&*)M.
   timespacex 'res=:(i.4) A"0 table (i.11)'
0.0336829 3.54035e6
   res
┌───┬──────────────────────────────────────────┐
│A"0│0  1  2  3   4   5   6    7    8    9   10│
├───┼──────────────────────────────────────────┤
│0  │1  2  3  4   5   6   7    8    9   10   11│
│1  │2  3  4  5   6   7   8    9   10   11   12│
│2  │3  5  7  9  11  13  15   17   19   21   23│
│3  │5 13 29 61 125 253 509 1021 2045 4093 8189│
└───┴──────────────────────────────────────────┘

PS: "0 ทำหน้าที่สร้าง A ให้กับองค์ประกอบแต่ละตัวแทนที่จะกลืนไปทางซ้ายและขวาแถวและสร้างความยาวผิดพลาด แต่ไม่จำเป็นสำหรับเช่น 9 = 2 A 3


codegolf.stackexchange.com/a/40174/48934คุณถูกทุบแล้ว!
Leun Nun

0

APL, 31

{⍺=0:⍵+1⋄⍵=0:1∇⍨⍺-1⋄(⍺-1)∇⍺∇⍵-1}

ตรงไปตรงมาสวย ใช้อักขระ once หนึ่งครั้งเพื่อบันทึกหนึ่งไบต์โดยการกลับอาร์กิวเมนต์ รับ m เป็นอาร์กิวเมนต์ซ้ายและ n เป็นอาร์กิวเมนต์ขวา

TryAPL.org


0

ทับทิม, 65

h,a={},->m,n{h[[m,n]]||=m<1?(n+1):(n<1?a[m-1,1]:a[m-1,a[m,n-1]])}

คำอธิบาย

นี่เป็นการแปลอัลกอริทึมที่ตรงไปตรงมาในคำอธิบายปัญหา

  • อินพุตถูกใช้เป็นอาร์กิวเมนต์สำหรับแลมบ์ดา สองIntegers ที่คาดว่า
  • สำหรับความเร็วและหลีกเลี่ยงข้อผิดพลาดของกองล้นคำตอบ memoized Hash hใน ||=ผู้ประกอบการจะใช้ในการคำนวณมูลค่าที่ไม่ได้คำนวณไว้ก่อนหน้านี้

a[3,10] คำนวณใน ~ 0.1 วินาทีบนเครื่องของฉัน

นี่คือรุ่นที่ไม่ได้แต่งแต้ม

h = {}
a = lambda do |m,n|
  h[[m,n]] ||= if m < 1 
    n + 1
  elsif n < 1
    a[m-1,1]
  else
    a[m-1,a[m,n-1]]
  end
end

a[3,10]โยน SystemStackError บนเครื่องของฉัน ...
TuxCrafting

Golf nitpicks: คุณสามารถเปลี่ยนm<1?(n+1):(n<1?a[m-1,1]:a[m-1,a[m,n-1]])เป็นm<1?n+1:a[m-1,n<1?1:a[m,n-1]]
เพียง Beautiful Art


0

Java, 274 ไบต์

import java.math.*;class a{BigInteger A(BigInteger b,BigInteger B){if(b.equals(BigInteger.ZERO))return B.add(BigInteger.ONE);if(B.equals(BigInteger.ZERO))return A(b.subtract(BigInteger.ONE),BigInteger.ONE);return A(b.subtract(BigInteger.ONE),A(b,B.subtract(BigInteger.ONE)));}}

มันคำนวณA(3,10)ในไม่กี่วินาทีและให้หน่วยความจำที่ไม่มีที่สิ้นสุดและพื้นที่สแต็คก็สามารถคำนวณการรวมกันของbและBตราบใดที่ผลที่ได้ต่ำกว่า 2 2147483647 -1


ฉันรู้ว่ามันใช้เวลาสักพัก แต่คุณสามารถตีกอล์ฟนี้ได้ถึง185 ไบต์ :import java.math.*;BigInteger A(BigInteger b,BigInteger B){return b.equals(B.ZERO)?B.add(B.ONE):B.equals(B.ZERO)?A(b.subtract(B.ONE),B.ONE):A(b.subtract(B.ONE),A(b,B.subtract(B.ONE)));}
Kevin Cruijssen

0

Ceylon, 88 87 85

alias I=>Integer;I a(I m,I n)=>m<1then n+1else(n<1then a(m-1,1)else a(m-1,a(m,n-1)));

นี่คือการดำเนินการที่ตรงไปตรงมา จัดรูปแบบ:

alias I => Integer;
I a(I m, I n) =>
        m < 1
        then n + 1
        else (n < 1
            then a(m - 1, 1)
            else a(m - 1, a(m, n - 1)));

นามแฝงช่วยประหยัดเพียงหนึ่งไบต์โดยไม่ได้ (ด้วยการเขียนIntegerแทนI) เราจะได้รับถึง 86 ไบต์ สามารถบันทึกอีกสองไบต์โดยแทนที่== 0ด้วย< 1สองครั้ง

ด้วยการตั้งค่าเริ่มต้นของceylon runมันจะทำงานได้ถึงA(3,12) = 32765(และA(4,0) = 13) แต่A(3,13)(และดังนั้นจึงA(4,1)) จะโยนข้อผิดพลาดล้นสแต็ค ( A(3,12)ใช้เวลาประมาณ 5 วินาทีA(3,11)ประมาณ 3 วินาทีในคอมพิวเตอร์ของฉัน)

การใช้ceylon run-js(เช่นการเรียกใช้ผลลัพธ์ของการรวบรวม JavaScript บน node.js) นั้นช้ากว่ามาก (ต้องใช้ 1 นาที 19 วินาทีA(3,10)) และตัวแบ่งแล้วสำหรับA(3, 11)ด้วย»ขนาดสแต็กการโทรสูงสุดเกิน« (ใช้การตั้งค่าเริ่มต้น) หลังจากใช้ 1 ขั้นต่ำ 30 วินาที


ศรีลังกาโดยไม่มีการสอบถามซ้ำ, 228

ในฐานะโบนัสนี่เป็นเวอร์ชันที่ไม่เรียกซ้ำ (อีกต่อไปแน่นอน แต่มีภูมิต้านทานต่อการโอเวอร์โฟลว์ - อาจได้รับข้อผิดพลาดหน่วยความจำไม่เพียงพอ)

import ceylon.collection{A=ArrayList}Integer a(Integer[2]r){value s=A{*r};value p=s.addAll;while(true){if(exists m=s.pop()){if(exists n=s.pop()){if(n<1){p([m+1]);}else if(m<1){p([n-1,1]);}else{p([n-1,n,m-1]);}}else{return m;}}}}

จัดรูปแบบ:

import ceylon.collection {
    A=ArrayList
}

Integer a(Integer[2] r) {
    value s = A { *r };
    value p = s.addAll;
    while (true) {
        if (exists m = s.pop()) {
            if (exists n = s.pop()) {
                if (n < 1) {
                    p([m + 1]);
                } else if (m < 1) {
                    p([n - 1, 1]);
                } else {
                    p([n - 1, n, m - 1]);
                }
            } else {
                // stack is empty
                return m;
            }
        }
    }
}

คอมพิวเตอร์ของฉันช้ากว่าเวอร์ชั่นที่เรียกซ้ำ: A(3,11)ใช้เวลา 9.5 วินาทีA(3,12)ใช้เวลา 34 วินาทีA(3,13)ใช้เวลา 2:08 นาทีA(3,14)ใช้เวลา 8:25 นาที (ตอนแรกฉันมีรุ่นที่ใช้ตัวทำซ้ำแบบขี้เกียจแทนสิ่งอันดับที่ฉันมีตอนนี้ซึ่งช้ากว่ามากด้วยขนาดเท่ากัน)

บิตที่เร็วกว่า (21 วินาทีสำหรับA(3,12)) (แต่ยังยาวอีกหนึ่งไบต์) เป็นรุ่นที่ใช้s.pushแทนs.addAllแต่จำเป็นต้องมีการโทรหลายครั้งเพื่อเพิ่มตัวเลขจำนวนมากเนื่องจากต้องใช้ Integer เพียงอันเดียว การใช้ LinkedList แทน ArrayList นั้นช้ากว่ามาก

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