Tính toán anh em họ Collatz


21

Xác định hàm f (n) cho số nguyên dương n như sau:

  • n / 2 , nếu n chẵn
  • 3 * n + 1 , nếu n là số lẻ

Nếu bạn liên tục áp dụng hàm này cho bất kỳ n lớn hơn 0, kết quả dường như luôn hội tụ thành 1 (mặc dù chưa ai có thể chứng minh điều đó). Khách sạn này được gọi là phỏng đoán Collatz .

Xác định thời gian dừng của số nguyên là số lần bạn phải chuyển qua chức năng Collatz f trước khi đạt 1. Đây là thời gian dừng của 15 số nguyên đầu tiên:

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

Chúng ta hãy gọi bất kỳ tập hợp số nào có cùng thời gian dừng với anh em họ Collatz . Ví dụ, 5 và 32 là anh em họ Collatz, với thời gian dừng là 5.

Nhiệm vụ của bạn: viết chương trình hoặc hàm lấy số nguyên không âm và tạo tập hợp anh em họ Collatz có thời gian dừng bằng số nguyên đó.

Đầu vào

Một số nguyên S không âm, được đưa ra thông qua STDIN, ARGV hoặc đối số hàm.

Đầu ra

Một danh sách của tất cả các số có thời gian dừng là S, được sắp xếp trong ascending trật tự. Danh sách có thể được xuất ra bởi chương trình của bạn, hoặc được trả về hoặc xuất ra bởi chức năng của bạn. Định dạng đầu ra rất linh hoạt: phân tách không gian, phân tách dòng mới hoặc bất kỳ định dạng danh sách tiêu chuẩn nào của ngôn ngữ của bạn đều tốt, miễn là các số có thể dễ dàng phân biệt với nhau.

Yêu cầu

Việc gửi của bạn phải cho kết quả chính xác cho bất kỳ S 30. Nó sẽ hoàn thành sau vài giây hoặc vài phút, không phải giờ hoặc ngày.

Ví dụ

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

Đây là một ý chính của đầu ra cho S = 30 .

Đây là : chương trình ngắn nhất tính theo byte thắng. Chúc may mắn!


Những gì của chu kỳ? Tôi không thấy đề cập đến việc tránh chu kỳ. Bởi vì với S = 5, có 3 giá trị [4, 5, 32] vì bạn có thể đi "1 - 2 - 4 - 1 - 2- 4"
JPMC

1
@JPMC Tránh chu kỳ được ngụ ý bởi định nghĩa về thời gian dừng. Thời gian dừng của 4 là 2, không phải 5, vì 2 là "số lần bạn phải vượt qua chức năng Collatz trước khi đạt 1."
DLosc

À, tha lỗi cho tôi. Tôi đã nghĩ rằng một số có thể có nhiều lần dừng, vì nhiều đường dẫn có thể dẫn đến nó. Nhưng đó là về việc xây dựng từ 1, không làm việc từ N. Xin lỗi về điều đó.
JPMC

1
@DLosc Pyth, tất nhiên.
isaacg

Câu trả lời:


7

Pyth, 26 24 21 byte

Su+yMG-/R3fq4%T6G1Q]1

Mã này chạy ngay lập tức cho S=30. Hãy tự mình thử: Trình diễn

Cảm ơn @isaacg đã lưu 5 byte.

Giải trình

Mã của tôi bắt đầu bằng 1và hoàn tác chức năng Collatz. Nó ánh xạ tất cả các số dcủa S-1bước đến 2*d(d-1)/3. Cái cuối cùng không phải lúc nào cũng hợp lệ.

                        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

Đó là một cách sử dụng tuyệt đẹp -F.
isaacg

1
Nếu bạn đặt một - ... 1số tiền trong tổng số giảm, bạn không cần giảm là một .u, cũng không phải -Fbên ngoài. Lưu 2 ký tự.
isaacg

@isaacg Cảm ơn. Tôi thực sự đã có điều này trong phiên bản trước, nhưng đã gỡ bỏ nó trong quá trình gỡ lỗi.
Jakube

3
Tôi đã mượn @ isaacg cho câu trả lời của riêng tôi. Tôi đã dành hàng giờ cố gắng tìm mã ngắn nhất để loại bỏ các bản sao, nhưng đây là giải pháp thanh lịch nhất. Ngoài ra, tôi thực sự thích việc bạn sử dụng một tuple để loại bỏ các chỉ tiêu không hợp lệ. Đáng buồn thay, CJam không có bộ dữ liệu, nhưng tôi đã quản lý để ánh xạ các chỉ số không hợp lệ thành 1.
Dennis

@Jakube q4%d6tương đương !%hhd6, nhưng ngắn hơn 1 ký tự.
isaacg

8

Toán học, 98 92 89 byte

Giải pháp này giải quyết S = 30ngay lập tức:

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

Đây là một hàm không tên được lấy Slàm tham số duy nhất của nó và trả về một danh sách anh em họ Collatz.

Thuật toán là một tìm kiếm đầu tiên đơn giản. Anh em họ Collatz cho một số đã cho Slà tất cả các số nguyên có thể đạt được từ anh em họ Collatz cho S-1thông qua 2*nhoặc số lẻ có thể đạt được thông qua (n-1)/3. Chúng tôi cũng cần đảm bảo rằng chúng tôi chỉ sản xuất những số nguyên đạt được lần đầu tiên sau Scác bước, vì vậy chúng tôi theo dõi tất cả anh em họ trước đó pvà loại bỏ những số nguyên đó khỏi kết quả. Vì dù sao chúng tôi cũng đang làm điều đó, chúng tôi có thể lưu một vài byte bằng cách tính các bước từ tất cả các anh em họ trước đó (không chỉ những người từ đó S-1) để lưu một vài byte (điều đó làm cho nó chậm hơn một chút, nhưng không đáng chú ý đối với yêu cầu S).

Đây là một phiên bản dễ đọc hơn một chút:

(
  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 byte

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

Gọi như thế f(30). n = 30là khá nhiều ngay lập tức.

(Cảm ơn @DLosc về ý tưởng đệ quy bằng cách klà một số chứ không phải là danh sách anh em họ và một vài byte. Cảm ơn @isaacg đã bỏ ~-.)

Biến thể này ngắn hơn nhiều, nhưng không may mất quá nhiều thời gian do phân nhánh theo cấp số nhân:

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

Thú vị - giải pháp ban đầu của tôi rất giống nhau, nhưng (lấy một vài tối ưu hóa từ bạn) đưa ra ngắn hơn 2 byte : 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ó kém hiệu quả hơn với các chức năng gọi nhưng vẫn thực hiện n = 30trong một giây.
DLosc

1
@DLosc Tôi thích ý tưởng của bạn và làm cho nó trở nên tốt hơn :)
Sp3000

Tốt đẹp! Dưới đây là 2 byte nữa: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 cảm ơn. Tôi vẫn thề rằng phải có một cách ngắn mạch tốt hơn mặc dù ...
Sp3000

Tôi nghĩ rằng điều đó ~-là không cần thiết bởi vì bạn đang sử dụng phép chia số nguyên.
isaacg

5

CJam, 29 26 byte

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

Tín dụng đến @isaacg vì ý tưởng của anh ấy để xóa 1 giây sau mỗi lần lặp, điều này giúp tôi tiết kiệm trực tiếp hai byte và một byte khác.

Dùng thử trực tuyến trong trình thông dịch CJam (sẽ kết thúc sau chưa đầy một giây).

Làm thế nào nó hoạt động

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 byte

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

Giải thích đến sớm. Đây là một phiên bản nhanh hơn nhiều so với cách tiếp cận "khá thẳng về phía trước" (xem nó trong lịch sử chỉnh sửa).

Dùng thử trực tuyến tại đây để N = 30chạy trong vài giây trên phiên bản trực tuyến và ngay lập tức trong Trình biên dịch Java


Điều này sẽ mất bao lâu cho các đầu vào lớn hơn? It should finish in seconds or minutes, not hours or days.
DLosc

Ah tôi thấy. Phiên bản Python tôi đã viết có vẻ mất khoảng 5 giờ cho N = 30.
DLosc

Phiên bản mới nhất chạy gần như ngay lập tức.
Trình tối ưu hóa

6
Có một lỗi trong mã của bạn. Các trường hợp thử nghiệm S=15không hoạt động.
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()

Khi x30, chương trình mất 15 phút và 29 giây.

Mở rộng

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)));
    }
}

Chỉ tò mò, điều này mất bao lâu cho S = 30?
Geobits

Điều này chỉ hoạt động trong Java 8, đúng không? Sử dụng javac 1.7.0_79trên Ubuntu đã cho tôi rất nhiều lỗi cú pháp.
DLosc

@DLosc Đúng; Tôi sẽ đề cập đến điều đó trong bài viết.
Ypnypn

Việc giới hạn điều kiện đầu cuối vòng lặp thành i > 1 && ++n <= x(bạn cũng có thể thả n++) dường như còn nhanh hơn chỉ với 5 ký tự nữa ... khoảng 3 phút cho S = 30 đối với tôi. Điều đó sẽ được chuyển sang an toàn dưới một phút nếu tôi cũng bao gồm .parallel(), nhưng vì đây là môn đánh gôn ...
hjk

1

Python 2, 118 byte

Chà, tôi đoán rằng tôi sẽ không đạt được điểm Python tốt nhất sau khi xem giải pháp của @ Sp3000. Nhưng nó có vẻ như là một vấn đề nhỏ thú vị, vì vậy tôi muốn thử một giải pháp độc lập:

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)

Điều tương tự trước khi tước khoảng trắng:

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)

Đây là một thực hiện rất trực tiếp của một tìm kiếm đầu tiên rộng. Trong mỗi bước, chúng ta có tập hợp với thời gian dừng kvà lấy ra tập hợp có thời gian dừng k + 1bằng cách thêm các tiền thân có thể có của mỗi giá trị ttrong tập từ bước k:

  • 2 * t luôn luôn là một người tiền nhiệm có thể.
  • Nếu tcó thể được viết như sau 3 * u + 1, nơi ulà một số lẻ được không 1, sau đó ulà một người tiền nhiệm là tốt.

Mất khoảng 0,02 giây để chạy N = 30trên MacBook Pro của tôi.


Nói chung, s.add(x)là không cần thiết trong một golf vì bạn thường có thể làm s|={x}thay thế. Ngoài ra, sử dụng ~-xthay vì (x+1)tiết kiệm trên ngoặc. Nhưng nếu không, công việc tốt :)
Sp3000

@ Sp3000 Cảm ơn. Tôi không thể dễ dàng thay thế thứ hais.add() vì nó trở thành một bài tập và không thể là một phần của biểu thức nữa. Nó làm việc cho người đầu tiên. Các forvòng lặp dựa trên quầy luôn luôn là loại dài dòng. Tôi nghĩ rằng tôi có thể rút ngắn nó bằng cách sử dụng một whilevòng lặp, nhưng hóa ra nó có cùng độ dài.
Reto Koradi

Thay vì forvòng lặp, vì bạn không sử dụng đầu vào theo bất kỳ cách nào khác mà bạn có thể làm exec"..."*input()thay vào đó :)
Sp3000

1

PHP 5,4+, 178 byte

Chức năng

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);}

Kiểm tra & đầu ra

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) chạy trong 0,24 giây * , trả về 732 phần tử. Một cặp vợ chồng là

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

* Để tiết kiệm byte, tôi phải thêm ksortarray_keysở mọi bước. Sự lựa chọn duy nhất khác mà tôi có là tạo một hàm bao bọc nhỏ gọi c()và sau đó gọi array_keysksortkết quả một lần. Nhưng do thời gian vẫn còn khá linh hoạt, tôi quyết định thực hiện cú đánh hiệu năng cho số byte thấp. Nếu không có sự sắp xếp và xử lý thích hợp, thời gian là 0,07 giây trung bình cho S (30).

Nếu bất cứ ai có bất kỳ cách thông minh nào để xử lý đúng cách chỉ một lần mà không cần quá nhiều byte, xin vui lòng cho tôi biết! (Tôi lưu trữ số của mình dưới dạng các khóa mảng, do đó sử dụng array_keysksort)


0

Ngôn ngữ 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
Xin chào và chào mừng đến với PPCG! Vì đây là một cuộc thi đánh gôn , bạn sẽ muốn làm cho mã của mình càng ngắn càng tốt. Ngoài ra, vui lòng bao gồm tên của ngôn ngữ trong bài viết của bạn.
Alex A.

Bạn có thể nhấn {}nút để định dạng mã của mình, điều mà tôi đã làm cho bạn. Nhưng như Alex nói, vui lòng thêm tên ngôn ngữ (C?) Và thử chơi gôn :) Nhưng xin chào mừng!
Sp3000

@ Sp3000 cảm ơn vì đã giúp định dạng mã
gió

Chức năng fkhông hoạt động đúng. Với s=5, tôi nhận được một loạt các kết quả không chính xác. if (r == s)return true;nên return (r==s), vì fsẽ không trả lại bất kỳ ý nghĩa nào khi (r < s). Ngoài ra, tôi nghĩ rằng bạn nên tuyên bố itrong fkhi long, vì nó sẽ tràn khá nhanh chóng cho một số giá trị.
Dennis

@Dennis cảm ơn :) nó nên như vậyreturn (r==s);
gió
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.