Thời gian biểu diễn thập phân


16

Viết hàm lấy một số nguyên dương n duy nhất và trả về khoảng thời gian biểu diễn thập phân là 1 / n .

Các trường hợp thử nghiệm:

1 -> 1               # 1/1 = 1.0000...... = 1._0
2 -> 1               # 1/2 = 0.5000...... = 0.5_0
3 -> 1               # 1/3 = 0.3333...... = 0._3
7 -> 6               # 1/7 = 0.14285714.. = 0._142857
13 -> 6
14 -> 6
123 -> 5
345 -> 22
654 -> 108
12345 -> 822
67890 -> 120

Đây là . Xây dựng hoặc thư viện trả lại thời gian trực tiếp không được phép. Các số lên đến ít nhất 100000 sẽ hoạt động trong thời gian hợp lý (nhiều nhất là vài phút).


Câu hỏi nêu rõ rằng "các số lên đến ít nhất 100000 nên hoạt động trong thời gian hợp lý", nhưng chương trình có phải đưa ra câu trả lời đúng cho các số lớn hơn số này không? Hoặc có thể chấp nhận sử dụng thuật toán chỉ chính xác đến 100000 không?
FireFly

1
Thuật toán @FireFly phải cung cấp câu trả lời đúng.
Howard

2
Tại sao 1 trả lại 1? Tôi sẽ nghĩ 0?
TimTech

@Timtech1.00000000000000000000000000000000000
Cruncher

@Cruncher Ồ cảm ơn, tôi hiểu rồi.
TimTech

Câu trả lời:


11

APL, 19 ký tự / byte *

{(↑⍳⍨1∘↓)⌽⍵|10x*⍳⍵}

Nars2000 . Phiên bản trước đã sai trên một số con số, điều này nên đúng. Tôi tự kiểm tra nó trên tất cả các số lên tới 50.

Một lần nữa, tín dụng đến Ben Reich cho ý tưởng nhìn vào thời kỳ10^i (mod x)

Khung nhìn bùng nổ

{                     ⍳⍵}   generate all naturals up to the argument ⍵
                 10x*       raise 10 to each of them, with unlimited precision
              ⍵|            compute the respective remainders mod ⍵
            ⌽               reverse the list
 (  ⍳⍨    )                 (fork) find the position of the first occurrence
  ↑                         of the fist element of the list
       1∘↓                  in the remainder of the list

Ví dụ

      {(↑⍳⍨1∘↓)⌽⍵|10x*⍳⍵}¨1 2 3 7 13 14 123 345 654 12345 67890
1 1 1 6 6 6 5 22 108 822 120

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
*: APL có thể được viết bằng bộ ký tự byte đơn (di sản) riêng để ánh xạ các ký hiệu APL đến các giá trị 128 byte trên. Do đó, với mục đích ghi điểm, một chương trình gồm các ký tự N chỉ sử dụng các ký tự ASCII và các ký hiệu APL có thể được coi là dài N byte.


Tôi không thể có câu trả lời chính xác cho ví dụ đầu vào 20. Bạn có thể vui lòng xác minh?
Howard

Tôi đã làm theo các ví dụ bạn đã đăng. Trong ví dụ của bạn, 1/2 = 0,5 -> 1, do đó, tự nhiên 1/20 = 0,05 -> 2. Bạn đang nhận được gì?
Tobia

Câu trả lời đúng sẽ là 1, kể từ 1/20 = 0,05_0_.
Howard

Tôi hiểu rồi. Hãy cho tôi một chút, tôi sẽ xem lại câu trả lời của mình.
Tobia

4Có vẻ như nó sẽ cung cấp cho các câu trả lời sai quá, bởi vì 10 != 100 (mod 4).
Peter Taylor

7

GolfScript ( 42 27)

{:x)1\[{.10*x%}*]-1%(?)}:P;

Thời gian chuẩn: 5 giây. Mã điểm chuẩn:

'"The time is #{Time.now#1
}"'~ puts
[1 2 3 7 13 14 123 345 654 12345 67890 99991]{[P]p}%
'"The time is #{Time.now#2
}"'~ puts

Tín dụng cho Ben Reich cho ý tưởng cốt lõi của việc nhìn vào thời kỳ 10^i (mod x).

Giải trình

Khoảng thời gian pđược định nghĩa là số nguyên dương nhỏ nhất sao cho tất cả ichúng ta có đủ lớn frac(10^i * 1/x) = frac(10^(i+p) * 1/x). Chúng ta có thể đơn giản hóa một chút để frac(10^i / x) = frac(10^(i+p) / x). Bây giờ, frac(a / x) = frac(b / x)iff a == b (mod x), vì vậy chúng tôi đang tìm số nguyên dương nhỏ nhất sao cho đủ lớn i: 10^i == 10^(i+p) (mod x).

Giả sử 10^i == 10^(i+p) (mod x). Sau đó 10^(i+1) == 10 * 10^i == 10 * 10^(i+p) == 10^(i+p+1) (mod x); Vì vậy, một khi chúng ta có được sự lặp lại, chúng ta đang ở trong một chu kỳ không thể phá vỡ.

Chỉ có xcác giá trị riêng biệt (mod x), vì vậy theo nguyên tắc pigeonhole, chúng ta phải có sự lặp lại trong các x + 1giá trị đầu tiên của 10^i (mod x).

Vì vậy, những gì mã ở trên là để tính x + 2giá trị của 10^i (mod x)*. Sau đó, cái cuối cùng được đảm bảo là sự lặp lại, và bằng cách đảo ngược danh sách và tìm kiếm nó, tôi có thể tìm thấy sự xuất hiện gần đây nhất. Hơn nữa, vì tôi chỉ thực hiện một tìm kiếm nên đây là thời gian giả.

* Một phụ là để xử lý các trường hợp đặc biệt x = 1, bởi vì tôi không giảm 10^0 (mod x)và vì vậy tôi muốn được tìm kiếm một 0trong [1].


Tuyệt vời! Tôi đã xóa câu trả lời của mình vì một giải pháp tốt hơn! -
Ben Reich

7

Golfscript - 26 byte

{:i.)+.,{;10*i%.}%i>|,}:f;

Chỉnh sửa: được cập nhật thành đầu ra 1nếu dấu thập phân chấm dứt, thay vì độ dài của biểu diễn thập phân.

Một phiên bản khá hiệu quả. Giá trị 67890 chạy trong khoảng 10 giây và 99991 khoảng 20 giây. Nó chậm hơn một chút so với trước đây (nhanh gần một nửa), bởi vì phạm vi được lặp lại đã tăng gấp đôi, nửa đầu trong số đó bị bỏ qua.

Thay thế, cũng 26 byte

{:i.)+.n*{*i%.}%i>)^^,}:f;

Cái này hoạt động bằng cách lặp qua chuỗi "\n"*(2*i+1), trong đó igiá trị được truyền cho hàm. Giá trị được truyền cho khối mỗi lần là giá trị thứ tự của "\n", là 10 .

Các )^^là một chút của một công việc xung quanh. Khi bạn hủy một ký tự từ một chuỗi, kết quả là giá trị thứ tự của ký tự bị xóa, như đã đề cập ở trên. Tuy nhiên, việc nối lại giá trị đó trở lại sẽ nối thêm biểu diễn chuỗi của số đó, thay vì ký tự - hành vi khá không đối xứng và theo tôi là một lỗ hổng thiết kế. Nếu bạn thực sự muốn làm điều đó, việc xâu chuỗi trước sẽ chỉ tốn một byte.

Một bản sao bổ sung của giá trị cuối cùng đã có trên ngăn xếp, vì vậy tôi xóa lại giá trị cuối cùng ), xor nó với chuỗi và sau đó xor nó một lần nữa, để bất kỳ ký tự nào được thêm hoặc xóa bởi xor đầu tiên đều được khôi phục. Nếu int op stringđược coi là một ký tự, thay vì đại diện chuỗi của nó, )^^có thể được thay thế bằng |.

Lưu ý rằng trong khi các chuỗi (trong Golfscript được lưu dưới dạng một mảng int) sẽ hiển thị giá trị của từng mod 256 ký tự , thì các giá trị của mỗi ký tự có thể nằm ngoài phạm vi này. Khi kiểm tra tính duy nhất (thông qua các thao tác thiết lập) hoặc tính chứa (thông qua ?), đó là giá trị thực được so sánh, thay vì giá trị hiển thị.

Một tệp vá cho trình thông dịch Golfscript hiện tại :

61c61
<       to_gs
---
>       Gstring.new([self])

Ở trên sẽ chỉ ảnh hưởng đến hành vi của string op int(và ngược lại), opmột trong những
+-|&^. Mọi thứ khác vẫn không bị ảnh hưởng, bao gồm cả hành vi của Gint`.

Giải pháp 24 byte sau đây sẽ trở thành hợp lệ:

{:i.)+.n*{*i%.}%i>|,}:f;

Và điều này cũng sửa chữa rất nhiều công việc thực sự xấu xí khác .


Python - 48 byte

f=lambda n:len(set(10**-~i%n for i in range(n)))

Không phải là giải pháp hiệu quả nhất, nhưng hợp lý cho các giá trị dưới 100000 .

FWIW, phần tử cốt lõi giống hệt với giải pháp của tôi để Tạo số chu kỳ theo số thập phân .

Một phiên bản hiệu quả hơn của cùng một mã ( 70 byte ):

 def f(n):
  a=[];i=10%n
  while i not in a:a+=i,;i=i*10%n
  return len(a)

Giá trị 99991 mất ít hơn một giây.


@PeterTaylor đó orlà mảng trên một chuỗi rỗng. Bởi vì đó là một hoạt động khôn ngoan, tất cả các bản sao được loại bỏ trước.
Primo

Nhưng chuỗi rỗng đến từ đâu? Nếu chức năng là độc lập, tôi nghĩ bạn sẽ phải tốn thêm một byte và tạo ra nó .|.
Peter Taylor

1
@PeterTaylor đã sửa .
primo

1
Thay đổi hành vi string int +sẽ phá vỡ rất nhiều chương trình. Tôi không chắc chắn tần suất các op khác được sử dụng trên cặp loại đó.
Peter Taylor

@PeterTaylor Tôi đồng ý, nó sẽ. Nhưng xem xét: chuyển đổi int để char: []+''+vs ''+. Nối int, như char, để chuỗi: []++vs +. Apend int, như đại diện chuỗi, chuỗi: +vs `+. Trong triển khai hiện tại của nó, int''+đồng nghĩa với int`, có vẻ lãng phí khi xem xét tính dài dòng của việc buộc phải mảng, và sau đó ép buộc thành một chuỗi nếu bạn muốn char ascii.
primo

3

GolfScript, 48 47 46

Cảm ơn @PeterTaylor vì đã cắt hai ký tự.

{2{1$1$%!{.@\/\d}*}:d~;5d;9{2$%}{10*9+}/+,}:f;

Tôi đã thử sử dụng J, nhưng nó tiếp tục cho tôi tất cả các loại kết quả lạ.

Kiểm tra trực tuyến

Điều này về cơ bản chia 2 và 5 cho số (2 và 5 là các thừa số nguyên tố của 10 và các đối ứng của chúng chấm dứt và giải mã thuật toán), sau đó số nguyên n thấp nhất sao cho số kết quả chia 10 ^ n - 1 là thời kỳ


3
Nếu bạn biết đó sẽ là cuộc gọi đầu tiên đến chức năng của bạn thì bạn có thể nội tuyến định nghĩa ở đó. Tức là thay vì {...}:d;...dbạn tiết kiệm 1 char với...{...}:d~
Peter Taylor

@PeterTaylor cảm ơn, đã không nghĩ về điều đó
Biến động

1
Đã nhận xét với Ben về việc không rời khỏi fngăn xếp, tôi nhận thấy rằng bạn cũng đang làm điều đó. Bạn thực sự nên thêm một ;chức năng để bật chức năng để so sánh công bằng với các ngôn ngữ khác.
Peter Taylor

2
Một tối ưu hóa vi mô khác: int array ,)\;có thể rút ngắn thành int array +,.
Peter Taylor

2

Perl, 52 ký tự

sub f{($p,%r)=1;1until$r{$p=$p*10%$_[0]}++;~~keys%r}

Đây là một thực hiện không phức tạp của cách tiếp cận trực tiếp. (May mắn là cách tiếp cận trực tiếp cũng khá hiệu quả: nhờ số học modulo, toán học không bao giờ phải xử lý một số nhiều hơn 10 lần giá trị đầu vào.)

Vì thử thách đã chỉ định một hàm, tôi cảm thấy bắt buộc (tái) khởi tạo các biến của mình, điều mà tôi không bận tâm khi làm cho một chương trình hoàn chỉnh. Tương tự, ~~câu lệnh cuối cùng là không cần thiết nếu hàm có thể chắc chắn nó sẽ được gọi trong ngữ cảnh vô hướng.


Hãy thử đầu vào 20nơi nó mang lại kết quả sai.
Howard

2

Clojure, 102, 117, 115, 106

không định dạng:

(defn r([n](r{}(iterate #(mod(* % 10)n)10)0))([a[f & s]i](if(a f)(- i(a f))(recur(assoc a f i)s(inc i)))))

định dạng:

(defn r
  ([n] (r {} (iterate #(mod (* % 10) n) 10) 0))
  ([a [f & s] i]
    (if (a f)
      (- i (a f))
      (recur
        (assoc a f i)
        s
        (inc i)))))

Chạy quy mô thời gian với thời gian. Gần như ngay lập tức trên máy tính của tôi cho các giá trị mẫu.

Về cơ bản, điều này tính toán kết quả của phép trừ sau mỗi bước trong phép chia dài. Một chu kỳ được phát hiện nếu tại bất kỳ điểm nào số đó giống với chu kỳ đã được tính trước nó.


Các mã phá vỡ với đầu vào 20. Bạn có thể vui lòng xác minh?
Howard

Bạn nói đúng, giải pháp trên bị lỗi. Gonna xem nếu tôi có thể sửa chữa nó.
RedDeckWins

Sản lượng dự kiến ​​cho 20 là gì?
RedDeckWins

Câu trả lời đúng sẽ là 1.
Howard

Nên đi, thuật toán đầu tiên sẽ thất bại ở nhiều đầu vào, ví dụ 12 và 20.
RedDeckWins

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.