กำลังคำนวณลูกพี่ลูกน้อง Collatz


21

นิยามฟังก์ชันf (n)สำหรับจำนวนเต็มบวกnดังนี้:

  • n / 2หากnเป็นเลขคู่
  • 3 * n + 1ถ้าnเป็นเลขคี่

หากคุณใช้ฟังก์ชั่นนี้ซ้ำ ๆ กับn ที่มากกว่า 0 ผลลัพธ์จะดูเหมือนว่าจะรวมกันเป็น 1 เสมอ (แม้ว่าจะยังไม่มีใครพิสูจน์ได้) สถานที่แห่งนี้เป็นที่รู้จักกันCollatz คาดเดา

กำหนดเวลาหยุดของจำนวนเต็มตามจำนวนครั้งที่คุณต้องผ่านฟังก์ชัน Collatz fก่อนที่จะถึง 1 นี่คือเวลาหยุดของจำนวนเต็ม 15 ตัวแรก:

1  0
2  1
3  7
4  2
5  5
6  8
7  16
8  3
9  19
10 6
11 14
12 9
13 9
14 17
15 17

ขอเรียกชุดตัวเลขใด ๆ ที่มีเวลาหยุดเดียวกันญาติ Collatz ตัวอย่างเช่น 5 และ 32 เป็นลูกพี่ลูกน้อง Collatz โดยมีเวลาหยุด 5

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

อินพุต

จำนวนเต็มที่ไม่ใช่ค่าลบ S ที่กำหนดผ่าน STDIN, ARGV หรืออาร์กิวเมนต์ของฟังก์ชัน

เอาท์พุต

รายการของตัวเลขทั้งหมดที่มีการหยุดเวลา S A, เรียงในขึ้นเพื่อ รายการของคุณอาจถูกส่งออกโดยโปรแกรมของคุณหรือส่งคืนหรือส่งออกโดยฟังก์ชั่นของคุณ รูปแบบผลลัพธ์มีความยืดหยุ่น: คั่นด้วยช่องว่าง, คั่นด้วยบรรทัดใหม่หรือรูปแบบรายการมาตรฐานของภาษาของคุณใช้ได้ตราบใดที่ตัวเลขสามารถแยกความแตกต่างได้อย่างง่ายดาย

ความต้องการ

การส่งของคุณจะต้องให้ผลลัพธ์ที่ถูกต้องสำหรับ S-30 ใด ๆ มันควรจะเสร็จในไม่กี่วินาทีหรือนาทีไม่ใช่ชั่วโมงหรือวัน

ตัวอย่าง

0  -> 1
1  -> 2
5  -> 5, 32
9  -> 12, 13, 80, 84, 85, 512
15 -> 22, 23, 136, 138, 140, 141, 150, 151, 768, 832, 848, 852, 853, 904, 906, 908, 909, 5120, 5376, 5440, 5456, 5460, 5461, 32768

นี่คือสรุปสาระสำคัญของการส่งออกสำหรับS = 30

นี่คือ : โปรแกรมที่สั้นที่สุดในหน่วยไบต์ชนะ โชคดี!


รอบอะไร ฉันไม่เห็นพูดถึงการหลีกเลี่ยงวงจร เพราะสำหรับ S = 5 มี 3 ค่า [4, 5, 32] เพราะคุณสามารถไปที่ "1 - 2 - 4 - 1 - 2- 4"
JPMC

1
@JPMC การหลีกเลี่ยงวงจรถูกกำหนดโดยความหมายของเวลาหยุด เวลาหยุดของ 4 คือ 2 ไม่ใช่ 5 เพราะ 2 คือ "จำนวนครั้งที่คุณต้องผ่านมันผ่านฟังก์ชัน Collatz ก่อนถึง 1"
DLosc

อ๊ะยกโทษให้ฉันด้วย ฉันคิดว่าตัวเลขอาจมีเวลาหยุดหลายครั้งเนื่องจากหลายเส้นทางสามารถนำไปสู่ แต่นั่นก็เกี่ยวกับการสร้างจาก 1 ไม่ใช่การทำงานจาก N. ขออภัยเกี่ยวกับเรื่องนี้
JPMC

1
@DLosc Pyth แน่นอน
isaacg

1
ที่เกี่ยวข้อง แต่ไม่ได้เล่นกอล์ฟ: math.stackexchange.com/q/470782/20792และmath.stackexchange.com/q/1243841/20792
Pureferret

คำตอบ:


7

Pyth, 26 24 21 ไบต์

Su+yMG-/R3fq4%T6G1Q]1

S=30รหัสนี้จะทำงานได้ทันทีสำหรับ ลองด้วยตัวคุณเอง: สาธิต

ขอบคุณ @isaacg สำหรับการบันทึก 5 ไบต์

คำอธิบาย

รหัสของฉันเริ่มต้นด้วย1และยกเลิกฟังก์ชั่น Collatz มันแผนที่ตัวเลขทั้งหมดdของS-1ขั้นตอนในการและ2*d (d-1)/3คนสุดท้ายในไม่ถูกต้องว่า

                        implicit: Q = input number
                   ]1   start with G = [1]
 u                Q     apply the following function Q-times to G:
                          update G by
   yMG                      each number in G doubled
  +                       +
          fq4%T6G           filter G for numbers T, which satisfy 4==T%6
       /R3                  and divide them by 3
      -          1          and remove 1, if it is in the list
                            (to avoid jumping from 4 to 1)
S                       sort the result and print

-Fนั่นคือการใช้งานที่สวยงามของ
isaacg

1
หากคุณใส่- ... 1ผลรวมโดยรอบในการลดลงคุณไม่จำเป็นต้องมีการลดที่จะเป็น.uหรือ-Fนอก บันทึก 2 ตัวอักษร
isaacg

@isaacg ขอบคุณ ที่จริงฉันมีสิ่งนี้ในรุ่นก่อนหน้า แต่ลบออกระหว่างการแก้ไขข้อผิดพลาด
Jakube

3
ฉันยืม @ isaacg's สำหรับคำตอบของฉันเอง ฉันใช้เวลาหลายชั่วโมงในการค้นหารหัสที่สั้นที่สุดสำหรับการลบรายการที่ซ้ำกัน แต่นี่เป็นวิธีที่ยอดเยี่ยมที่สุด นอกจากนี้ฉันชอบที่คุณใช้ tuple เพื่อละทิ้งความฉลาดที่ไม่ถูกต้อง น่าเศร้าที่ CJam ไม่มีสิ่งอันดับ แต่ฉันจัดการเพื่อทำแผนที่จำนวนที่ไม่ถูกต้องกับ 1
Dennis

@Jakube q4%d6เทียบเท่า!%hhd6แต่สั้นกว่า 1 อักขระ
isaacg

8

Mathematica, 98 92 89 ไบต์

วิธีนี้จะแก้ปัญหาS = 30ทันที:

(p={0};l={1};Do[l=Complement[##&@@{2#,Mod[a=#-1,2]#~Mod~3~Mod~2a/3}&/@l,p=p⋃l],{#}];l)&

นี่เป็นฟังก์ชันที่ไม่มีชื่อซึ่งใช้Sเป็นพารามิเตอร์เท่านั้นและส่งคืนรายการของ Collatz cousins

อัลกอริทึมเป็นการค้นหาแบบกว้างอย่างแรก ญาติ Collatz สำหรับรับSมีทั้งหมดจำนวนเต็มที่สามารถเข้าถึงได้จากญาติ Collatz สำหรับS-1ผ่านหรือเลขคี่ที่สามารถเข้าถึงได้ผ่านทาง2*n (n-1)/3เราต้องให้แน่ใจว่าเราจะผลิตจำนวนเต็มเหล่านั้นซึ่งถึงเป็นครั้งแรกหลังจากSขั้นตอนดังนั้นเราจึงติดตามญาติก่อนหน้านี้ทั้งหมดpและลบออกจากผลลัพธ์ เนื่องจากเรากำลังทำเช่นนั้นเราสามารถบันทึกสองสามไบต์โดยการคำนวณขั้นตอนจากลูกพี่ลูกน้องก่อนหน้าทั้งหมด (ไม่ใช่แค่เพียงจากS-1) เพื่อบันทึกไม่กี่ไบต์ (ที่ทำให้ช้าลงเล็กน้อย แต่ไม่เห็นได้ชัดสำหรับความต้องการS)

นี่เป็นเวอร์ชันที่อ่านได้มากกว่านี้เล็กน้อย:

(
  p = {0};
  l = {1};
  Do[
    l = Complement[
      ## & @@ {2 #, Mod[a = # - 1, 2] #~Mod~3~Mod~2 a/3} & /@ l,
      p = p ⋃ l
    ]~Cases~_Integer,
    {#}
  ];
  l
) &

5

Python 2, 86 83 75 73 71 ไบต์

f=lambda n,k=1:sorted([k][n:]or(k>4==k%6and f(n-1,k/3)or[])+f(n-1,k*2))

f(30)โทรเช่น n = 30ทันใดนั้นสวยมาก

(ขอบคุณ @DLosc สำหรับแนวคิดเรื่องการเรียกซ้ำโดยkเป็นตัวเลขแทนที่จะเป็นรายการของลูกพี่ลูกน้องและอีกสองสามไบต์ขอบคุณ @isaacg สำหรับการ~-ดรอป)

ตัวแปรนี้สั้นกว่ามาก แต่น่าเสียดายที่ใช้เวลานานเกินไปเนื่องจากการแยกแบบเอกซ์โพเนนเชียล:

f=lambda n,k=1:sorted([k][n:]or(k>4==k%6)*f(n-1,k/3)+f(n-1,k*2))

ที่น่าสนใจ - แก้ปัญหาเดิมของฉันเป็นคล้ายกันมาก แต่ (การเพิ่มประสิทธิภาพคู่จากคุณ) ออกมา 2 f=lambda d,n=1:d and sorted(sum((c(d-1,x)for x in[n*2]+[~-n/3][:n>4==n%6]),[]))or[n]ไบต์สั้น: มันมีประสิทธิภาพน้อยพร้อมฟังชั่น แต่ยังไม่n = 30อยู่ในภายใต้สอง
DLosc

1
@DLosc ฉันชอบความคิดของคุณและทำให้มันเป็นหนึ่งที่ดีกว่า :)
SP3000

ดี! นี่คืออีก 2 ไบต์ออก:f=lambda n,k=1:sorted([k][n:]or(k>4==k%6and f(n-1,~-k/3)or[])+f(n-1,k*2))
DLosc

@DLosc Ahaha ขอบคุณ ฉันยังสาบานว่าจะต้องมีวิธีลัดวงจรที่ดีกว่าแม้ว่า ...
Sp3000

ฉันคิดว่า~-มันไม่จำเป็นเพราะคุณใช้การหารจำนวนเต็ม
isaacg

5

CJam, 29 26 ไบต์

Xari{{2*_Cmd8=*2*)}%1-}*$p

เครดิตไปที่ @isaacg สำหรับความคิดของเขาในการลบ 1 หลังจากการทำซ้ำแต่ละครั้งซึ่งช่วยฉันสองไบต์โดยตรงและอีกหนึ่งทางอ้อม

ลองออนไลน์ในล่าม CJam (ควรเสร็จในเวลาไม่ถึงหนึ่งวินาที)

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

Xa       e# Push A := [1].
ri{      e# Read an integer from STDIN and do the following that many times:
  {      e# For each N in A:
    2*   e#     Push I := (N * 2) twice.
    _Cmd e#     Push (I / 12) and (I % 12).
     8=  e#     Push K := (I % 12 == 8).

         e#     (K == 1) if and only if the division ((N - 1) / 3) is exact and
         e#     yields an odd integer. In this case we can compute the quotient 
         e#     as (I / 12) * 2 + 1.

    *2*) e#     Push J := (I / 12) * K * 2 + 1.

         e#     This yields ((N - 1) / 3) when appropriate and 1 otherwise.
  }%     e# Replace N with I and J.
  1-     e# Remove all 1's from A.

         e# This serves three purposes:

         e# 1. Ones have been added as dummy values for inappropriate quotients.

         e# 2. Not allowing 1's in A avoids integers that have already stopped
         e#    from beginning a new cycle. Since looping around has been prevented,
         e#    A now contains all integers of a fixed stopping time.

         e# 3. If A does not contain duplicates, since the maps N -> I and N -> J
         e#      are inyective (exluding image 1) and yield integers of different
         e#      parities, the updated A won't contain duplicates either.

}*       e#
$p       e# print(sort(C))

4

CJam, 35 ไบต์

1]ri{_"(Z/Y*"3/m*:s:~\L+:L-_&0-}*$p

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

ลองมันออนไลน์ได้ที่นี่สำหรับN = 30ซึ่งจะทำงานในไม่กี่วินาทีในรูปแบบออนไลน์และทันทีในJava คอมไพเลอร์


สิ่งนี้จะใช้เวลานานสำหรับอินพุตที่ใหญ่กว่า It should finish in seconds or minutes, not hours or days.
DLosc

อ่าฉันเข้าใจแล้ว เวอร์ชัน Python ที่ฉันเขียนดูเหมือนจะใช้เวลาประมาณ 5 ชั่วโมงสำหรับ N = 30
DLosc

เวอร์ชันล่าสุดทำงานเกือบจะในทันที
เครื่องมือเพิ่มประสิทธิภาพ

6
มีข้อบกพร่องในรหัสของคุณ กรณีทดสอบS=15ไม่ทำงาน
Jakube

3

Java 8, 123

x->java.util.stream.LongStream.range(1,(1<<x)+1).filter(i->{int n=0;for(;i>1;n++)i=i%2<1?i/2:3*i+1;return n==x;}).toArray()

เมื่อxเป็น 30 โปรแกรมใช้เวลา 15 นาทีและ 29 วินาที

ขยาย

class Collatz {
    static IntFunction<long[]> f =
            x -> java.util.stream.LongStream.range(1, (1 << x) + 1).filter(i -> {
                int n = 0;
                for (; i > 1; n++)
                    i = i % 2 < 1 ? i / 2 : 3 * i + 1;
                return n == x;
            }).toArray();

    public static void main(String[] args) {
        System.out.println(Arrays.toString(f.apply(15)));
    }
}

แค่อยากรู้อยากเห็นสิ่งนี้ใช้เวลานานแค่ไหนสำหรับ S = 30?
Geobits

ใช้งานได้กับ Java 8 เท่านั้นใช่ไหม การใช้javac 1.7.0_79บน Ubuntu ทำให้ฉันมีข้อผิดพลาดทางไวยากรณ์มากมาย
DLosc

@DLosc ถูกต้อง; ฉันจะพูดถึงว่าในโพสต์
Ypnypn

การ จำกัด เงื่อนไขของเทอร์มินัลลูปเป็นi > 1 && ++n <= x(คุณสามารถดร็อปได้n++ด้วย) ดูเหมือนว่าจะเร็วขึ้นสำหรับตัวละครอีก 5 ตัว ... ประมาณ 3 นาทีสำหรับ S = 30 สำหรับฉัน ที่ได้รับการโกนได้อย่างปลอดภัยภายใต้นาทีถ้าฉันรวม.parallel()เกินไป แต่ตั้งแต่นี้เป็นรหัสกอล์ฟ ...
HJK

1

Python 2, 118 ไบต์

ฉันคิดว่าฉันจะไม่ได้คะแนน Python ที่ดีที่สุดหลังจากเห็นวิธีแก้ปัญหาของ @ Sp3000 แต่ดูเหมือนปัญหาเล็ก ๆ น้อย ๆ ที่สนุกสนานฉันจึงอยากลองวิธีแก้ปัญหาอิสระต่อไป:

s={1}
for k in range(input()):
 p,s=s,set()
 for t in p:s.add(2*t);t>4and(t-1)%6==3and s.add((t-1)/3)
print sorted(s)

สิ่งเดียวกันก่อนที่จะลอกช่องว่าง:

s={1}
for k in range(input()):
    p,s=s,set()
    for t in p:
        s.add(2 * t)
        t > 4 and (t - 1) % 6 == 3 and s.add((t - 1) / 3)
print sorted(s)

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

  • 2 * t เป็นรุ่นก่อนที่เป็นไปได้เสมอ
  • หากtสามารถเขียนเป็น3 * u + 1, ซึ่งuเป็นเลขคี่ที่ไม่ได้เป็น1เช่นนั้นuก็เป็นรุ่นก่อนเช่นกัน

ใช้เวลาประมาณ 0.02 วินาทีในการรันN = 30บน MacBook Pro ของฉัน


โดยทั่วไปs.add(x)ไม่จำเป็นในกีฬากอล์ฟตั้งแต่คุณจะสามารถทำs|={x}แทน นอกจากนี้ยังใช้~-xแทนการ(x+1)บันทึกในวงเล็บ แต่ไม่อย่างนั้นก็ทำได้ดี :)
Sp3000

@ Sp3000 ขอบคุณ ฉันไม่สามารถแทนที่วินาทีได้อย่างง่ายดายs.add()เพราะมันได้รับมอบหมายและไม่สามารถเป็นส่วนหนึ่งของการแสดงออกอีกต่อไป มันใช้ได้ผลกับอันแรก forลูปบนพื้นฐานของเคาน์เตอร์อยู่เสมอชนิดของ verbose เช่นกัน ฉันคิดว่าฉันสามารถทำให้สั้นลงได้โดยใช้whileลูป แต่มันกลับกลายเป็นเหมือนกันกับความยาวเท่ากัน
Reto Koradi

แทนที่จะเป็นforห่วงเนื่องจากคุณไม่ได้ใช้การป้อนข้อมูลในทางอื่น ๆ ที่คุณอาจจะสามารถทำexec"..."*input()แทน :)
SP3000

1

PHP 5.4+, 178 ไบต์

ฟังก์ชั่น

function c($s,$v=1,$p=[],&$r=[]){$p[]=$v;if(!$s--){return$r[$v][]=$p;}c($s,$v*2,$p,$r);is_int($b=($v-1)/3)&!in_array($b,$p)&$b%2?c($s,$b,$p,$r):0;ksort($r);return array_keys($r);}

ทดสอบ & เอาท์พุท

echo "0 - ".implode(',',c(0)).PHP_EOL;
// 0 - 1
echo "1 - ".implode(',',c(1)).PHP_EOL;
// 1 - 2
echo "5 - ".implode(',',c(5)).PHP_EOL;
// 5 - 5,32
echo "9 - ".implode(',',c(9)).PHP_EOL;
// 9 - 12,13,80,84,85,512
echo "15 - ".implode(',',c(15)).PHP_EOL;
// 15 - 22,23,136,138,140,141,150,151,768,832,848,852,853,904,906,908,909,5120,5376,5440,5456,5460,5461,32768

S (30) ทำงานใน0.24 วินาที *ส่งกลับองค์ประกอบ 732 เป็นคู่รักกัน

86,87,89,520,522,524,525,528, [ ... ] ,178956928,178956960,178956968,178956970,1073741824

* เพื่อประหยัดไบต์ฉันต้องเพิ่มksortและarray_keysในทุกขั้นตอน ตัวเลือกอื่น ๆ ที่ฉันมีคือการสร้างฟังก์ชั่น wrapper ขนาดเล็กที่โทรc()แล้วโทรออกarray_keysและksortผลลัพธ์หนึ่งครั้ง แต่เนื่องจากยังมีเวลาเหลือเฟือฉันจึงตัดสินใจที่จะแสดงเป็นจำนวนไบต์ต่ำ หากไม่มีการเรียงลำดับและการประมวลผลที่เหมาะสมเวลาเฉลี่ย0.07 วินาทีสำหรับ S (30)

หากใครมีวิธีที่ชาญฉลาดในการประมวลผลที่เหมาะสมเพียงครั้งเดียวโดยไม่ต้องมีไบต์เพิ่มเติมมากเกินไปโปรดแจ้งให้เราทราบ! (ฉันเก็บหมายเลขของฉันเป็นคีย์อาร์เรย์ดังนั้นการใช้array_keysและksort)


0

ภาษา C

#include <stdio.h>
#include <limits.h>    
const int s = 30;

bool f(long i)
{
    int r = 0;
    for(;;)
        if (i < 0 || r > s) return false;
        else if (i == 1) break;
        else{r ++;i = i % 2 ? 3*i + 1 : i/2;}
    return (r==s);
}

void main(){
    for(long i = 1; i < LONG_MAX; i++) if (f(i)) printf("%ld ", i);
}

5
สวัสดีและยินดีต้อนรับสู่ PPCG! เนื่องจากนี่เป็นการแข่งขันกอล์ฟรหัสคุณจะต้องทำให้รหัสของคุณสั้นที่สุด นอกจากนี้โปรดระบุชื่อภาษาในโพสต์ของคุณ
Alex A.

คุณสามารถกด{}ปุ่มเพื่อจัดรูปแบบรหัสของคุณซึ่งฉันได้ทำเพื่อคุณ แต่อย่างที่ Alex บอกกรุณาเพิ่มชื่อภาษา (C?) แล้วลองเล่นกอล์ฟ :) แต่ยินดีต้อนรับ!
Sp3000

@ Sp3000 ขอขอบคุณที่ช่วยฟอร์แมตโค้ด
ลมแรง

ฟังก์ชั่นfทำงานผิดปกติ ด้วยs=5ฉันได้รับผลลัพธ์ที่ไม่ถูกต้องจำนวนมาก if (r == s)return true;ควรจะเป็นreturn (r==s)เนื่องจากfจะไม่กลับ anytging meaninful (r < s)เมื่อ นอกจากนี้ผมคิดว่าคุณควรประกาศiในfเป็นlongเพราะมันจะล้นสวยได้อย่างรวดเร็วสำหรับบางค่า
เดนนิส

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