Làm thế nào để tôi tìm thấy giai thừa của một số dương?


18

Các thách thức:

Viết chương trình hoặc hàm nhập số dương và trả về giai thừa của nó .

Lưu ý: Đây là một câu hỏi . Xin đừng coi trọng câu hỏi và / hoặc câu trả lời. Thêm thông tin ở đây . Mỗi câu hỏi cũng là một câu hỏi , vì vậy câu trả lời được bình chọn cao nhất sẽ thắng.



4
-1, xin lỗi, vì chúng tôi đang nhận được một lượng lớn các câu hỏi
troll


Mã trolling đang trong quá trình loại bỏ, theo lập trường chính thức. Câu hỏi này có số lượng phiếu bầu hợp lý với nhiều câu trả lời, trong đó có nhiều câu được bình chọn cực kỳ cao. Nó chỉ nhận được hơn 50% phiếu "xóa" trong cuộc thăm dò ý kiến , nhưng điều độc đáo là nó nhận được rất nhiều câu trả lời và phiếu bầu, vì vậy tôi đang khóa nó vì ý nghĩa lịch sử.
Doorknob

Câu trả lời:


46

Đây là một vấn đề điện toán số rất đơn giản mà chúng ta có thể giải quyết với phép tính gần đúng của Stirling :

Công thức gần đúng của Stirling

Như bạn có thể thấy, công thức đó có căn bậc hai, mà chúng ta cũng sẽ cần một cách để tính gần đúng. Chúng tôi sẽ chọn cái gọi là "phương pháp Babylon" vì nó được cho là phương pháp đơn giản nhất:

Phương pháp Babylon

Lưu ý rằng tính toán căn bậc hai theo cách này là một ví dụ tốt về đệ quy.

Kết hợp tất cả lại với nhau trong một chương trình Python cung cấp cho chúng tôi giải pháp sau cho vấn đề của bạn:

def sqrt(x, n): # not the same n as below
    return .5 * (sqrt(x, n - 1) + x / sqrt(x, n - 1)) if n > 0 else x

n = float(raw_input())
print (n / 2.718) ** n * sqrt(2 * 3.141 * n, 10)

Với một sửa đổi đơn giản, chương trình trên có thể tạo ra một bảng các giai thừa:

1! =    0.92215
2! =    1.91922
3! =    5.83747
4! =   23.51371
5! =  118.06923
6! =  710.45304
7! = 4983.54173
8! = 39931.74015
9! = 359838.58817

Phương pháp này phải đủ chính xác cho hầu hết các ứng dụng.


16
+1 Sự đơn giản và chính xác của phương pháp này giúp nó trở thành người chiến thắng rõ ràng
Joe the Person

44

C #

Xin lỗi, nhưng tôi ghét chức năng đệ quy.

public string Factorial(uint n) {
    return n + "!";
}

1
Về mặt kỹ thuật, bạn đã hài lòng với bản tóm tắt! ;) +1 cho lạm dụng ngắn
WallyWest

36

Java

public int factorial ( int n ) {
switch(n){
case 0: return 1;
case 1: return 1;
case 2: return 2;
case 3: return 6;
case 4: return 24;
case 5: return 120;
case 6: return 720;
case 7: return 5040;
case 8: return 40320;
case 9: return 362880;
case 10: return 3628800;
case 11: return 39916800;
case 12: return 479001600;
default : throw new IllegalArgumentException();
}
}

16
Tôi đã thử nó - rất hiệu quả. Sẽ xuất xưởng với bản phát hành tiếp theo. :)
Julian

Bên cạnh "syndrom số ma thuật", đây thực sự có thể là một triển khai tốt miễn là n <13, ít ngăn xếp hơn nhiều. Viết nó "trường hợp 4: trả lại 4 * 3 * 2;" và bạn sẽ có một lớp học đàng hoàng, nhanh hơn nhiều so với lớp đệ quy cũ.
Fabinout

6
@Fabinout, việc thực hiện là chính xác ngay cả với n> = 13. 13!> Số nguyên.MAX_VALUE.
emory

21

Con trăn

Tất nhiên cách tốt nhất để giải quyết bất kỳ vấn đề nào là sử dụng các biểu thức thông thường:

import re

# adapted from http://stackoverflow.com/q/15175142/1333025
def multiple_replace(dict, text):
  # Create a regular expression  from the dictionary keys
  regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
  # Repeat while any replacements are made.
  count = -1
  while count != 0:
    # For each match, look-up corresponding value in dictionary.
    (text, count) = regex.subn(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
  return text

fdict = {
    'A': '@',
    'B': 'AA',
    'C': 'BBB',
    'D': 'CCCC',
    'E': 'DDDDD',
    'F': 'EEEEEE',
    'G': 'FFFFFFF',
    'H': 'GGGGGGGG',
    'I': 'HHHHHHHHH',
    'J': 'IIIIIIIIII',
    'K': 'JJJJJJJJJJJ',
    'L': 'KKKKKKKKKKKK',
    'M': 'LLLLLLLLLLLLL',
    'N': 'MMMMMMMMMMMMMM',
    'O': 'NNNNNNNNNNNNNNN',
    'P': 'OOOOOOOOOOOOOOOO',
    'Q': 'PPPPPPPPPPPPPPPPP',
    'R': 'QQQQQQQQQQQQQQQQQQ',
    'S': 'RRRRRRRRRRRRRRRRRRR',
    'T': 'SSSSSSSSSSSSSSSSSSSS',
    'U': 'TTTTTTTTTTTTTTTTTTTTT',
    'V': 'UUUUUUUUUUUUUUUUUUUUUU',
    'W': 'VVVVVVVVVVVVVVVVVVVVVVV',
    'X': 'WWWWWWWWWWWWWWWWWWWWWWWW',
    'Y': 'XXXXXXXXXXXXXXXXXXXXXXXXX',
    'Z': 'YYYYYYYYYYYYYYYYYYYYYYYYYY'}

def fact(n):
    return len(multiple_replace(fdict, chr(64 + n)))

if __name__ == "__main__":
    print fact(7)

1
Tất nhiên rồi :)
Pierre Arlaud

15

Haskell

Mã ngắn là mã hiệu quả, vì vậy hãy thử điều này.

fac = length . permutations . flip take [1..]

Tại sao nó trolling:

Tôi đã cười vào bất kỳ lập trình viên nào đã viết điều này ... Sự kém hiệu quả là đẹp. Cũng có thể không thể hiểu được đối với bất kỳ lập trình viên Haskell nào thực sự không thể viết một hàm giai thừa.

Chỉnh sửa: Tôi đã đăng bài này cách đây một thời gian, nhưng tôi nghĩ tôi sẽ làm rõ cho những người trong tương lai và những người không thể đọc Haskell.

Mã ở đây lấy danh sách các số từ 1 đến n, tạo danh sách tất cả các hoán vị của danh sách đó và trả về độ dài của danh sách đó. Trên máy của tôi, mất khoảng 20 phút cho 13!. Và sau đó phải mất bốn giờ cho 14! và sau đó hai ngày rưỡi trong 15!. Ngoại trừ tại một số điểm trong đó bạn hết bộ nhớ.

Chỉnh sửa 2: Trên thực tế, bạn có thể sẽ không hết bộ nhớ do đây là Haskell (xem bình luận bên dưới). Bạn có thể buộc nó phải đánh giá danh sách và giữ nó trong bộ nhớ bằng cách nào đó, nhưng tôi không biết đủ về việc tối ưu hóa (và không tối ưu hóa) Haskell để biết chính xác làm thế nào để làm điều đó.


Ghê gớm và rất thanh lịch, tất cả cùng một lúc.
PLL

1
Bạn có chắc chắn về vấn đề bộ nhớ? Tại bất kỳ thời điểm nào, bạn cần giữ trong bộ nhớ: - danh sách [1..n]. - Một hoán vị đặc biệt của [1..n], liên quan đến một thunk cho phần còn lại của hoán vị (đa thức trong n). - Bộ tích lũy cho lengthhàm.
John Dvorak

Điểm công bằng, có lẽ không thực sự. Không thực sự nghĩ về nó quá nhiều. Tôi sẽ thêm một bình luận ở phía dưới.
jgon

10

C #

Since this is a math problem, it makes sense to use an application specifically designed to solve math problems to do this calculation...

Step 1:

Install MATLAB. A trial will work, I think, but this super-complicated problem is likely important enough to merit purchasing the full version of the application.

Step 2:

Include the MATLAB COM component in your application.

Step 3:

public string Factorial(uint n) {
    MLApp.MLApp matlab = new MLApp.MLApp();
    return matlab.Execute(String.Format("factorial({0})", n);
}

Matlab for students starts at $100. Professional versions or site licenses can go way into the thousands.
Moshe Katz

4
Moshe Katz - justified because factorials.
Mike H.

9

C#

Yếu tố là một hoạt động toán học cấp cao hơn có thể khó tiêu hóa tất cả trong một lần. Giải pháp tốt nhất trong các vấn đề lập trình như thế này, là chia nhỏ một nhiệm vụ lớn thành các nhiệm vụ nhỏ hơn.

Bây giờ, n! được định nghĩa là 1 * 2 * ... * n, vì vậy, về bản chất, phép nhân lặp lại và phép nhân không là gì ngoài phép cộng lặp lại. Vì vậy, với ý nghĩ đó, những điều sau đây giải quyết vấn đề này:

long Factorial(int n)
{
    if(n==0)
    {
        return 1;
    }

    Stack<long> s = new Stack<long>();
    for(var i=1;i<=n;i++)
    {
        s.Push(i);
    }
    var items = new List<long>();
    var n2 = s.Pop();
    while(s.Count >0)
    {
        var n3 = s.Pop();
        items.AddRange(FactorialPart(n2,n3));
        n2 = items.Sum();
    }
    return items.Sum()/(n-1);
}

IEnumerable<long> FactorialPart(long n1, long n2)
{
    for(var i=0;i<n2;i++){
        yield return n1;
    }
}

Bạn có một nút cổ chai gửi tất cả điều này thông qua một CPU hoặc lõi, mà tôi nghĩ rằng tôi có thể đã giải quyết trong câu trả lời của mình :-)
Paul

9
#include <math.h>

int factorial(int n)
{
    const double g = 7;
    static const double p[] = { 0.99999999999980993, 676.5203681218851,
                                -1259.1392167224028, 771.32342877765313,
                                -176.61502916214059, 12.507343278686905,
                                -0.13857109526572012, 9.9843695780195716e-6,
                                1.5056327351493116e-7 };
    double z = n - 1 + 1;
    double x = p[0];
    int i;
    for ( i = 1; i < sizeof(p)/sizeof(p[0]); ++i )
        x += p[i] / (z + i);
    return sqrt(2 * M_PI) * pow(z + g + 0.5, z + 0.5)  * exp(-z -g -0.5) * x + 0.5;
}

Quỷ dữ:

  • Một cách tính toán chính xác 100% mà hoàn toàn bỏ lỡ quan điểm là thực hiện lặp đi lặp lại hoặc đệ quy.
  • Bạn không biết tại sao nó hoạt động và không thể khái quát nó để làm bất cứ điều gì khác.
  • Tốn kém hơn là chỉ tính toán với toán học số nguyên.
  • Mã "tối ưu" rõ ràng nhất ( z = n - 1 + 1) thực sự là tài liệu tự ghi nếu bạn biết điều gì đang xảy ra.
  • Để trolling thêm tôi nên tính toán p[]bằng cách sử dụng phép tính đệ quy của các hệ số chuỗi!

(Đó là xấp xỉ Lanczos của hàm gamma )


Có điểm nào ở - 1 + 1đây không? Trình biên dịch của tôi tối ưu hóa nó (nó không phải là số dấu phẩy động trong đó việc tối ưu hóa mã như thế này có thể nguy hiểm), vì vậy nó dường như không cần thiết.
Konrad Borowski

4
@xfix: double z = n - 1là một phần của hàm xấp xỉ của hàm gamma. Là + 1từ mối quan hệ mà gamma(n + 1) = n!cho số nguyên n.
Ben Jackson

9

Chúng ta đều biết từ trường đại học rằng cách hiệu quả nhất để tính toán nhân là thông qua việc sử dụng logarit. Rốt cuộc, tại sao người ta lại sử dụng bảng logarit trong hàng trăm năm?

Vì vậy, từ danh tính, a*b=e^(log(a)+log(b))chúng tôi tạo thành mã Python sau:

from math import log,exp

def fac_you(x):
    return round(exp(sum(map(log,range(1,x+1)))))

for i in range(1,99):
    print i,":",fac_you(i)

Nó tạo ra một danh sách các số từ 1đến x, (+1 cần thiết vì Python hút) tính toán logarit của từng số, tính tổng các số, tăng e lên lũy thừa của số tổng và cuối cùng làm tròn giá trị thành số nguyên gần nhất (vì Python hút) . Python có một hàm tích hợp để tính các giai thừa, nhưng nó chỉ hoạt động cho các số nguyên, vì vậy nó không thể tạo ra số lượng lớn (vì Python hút). Đây là lý do tại sao các chức năng trên là cần thiết.

Btw, một lời khuyên chung cho sinh viên là nếu một cái gì đó không hoạt động như mong đợi, có lẽ là do ngôn ngữ bị hút.


Ước gì tôi có thể đưa ra một số phiếu bầu bổ sung ở đó cho phần mô tả, nhưng Python hút
Mark K Cowan

1
Tôi đã cười nhạo "fac you"
Number9

8

Thật không may, Javascript thiếu một cách tích hợp để tính giai thừa. Nhưng, bạn có thể sử dụng ý nghĩa của nó trong tổ hợp để xác định giá trị tuy nhiên:

Giai thừa của một số n là số hoán vị của một danh sách có kích thước đó.

Vì vậy, chúng ta có thể tạo mọi danh sách số n chữ số, kiểm tra xem đó có phải là hoán vị không và nếu có, hãy tăng một bộ đếm:

window.factorial = function($nb_number) {
  $nb_trials = 1
  for($i = 0; $i < $nb_number; $i++) $nb_trials *= $nb_number
  $nb_successes = 0
  __trying__:
  for($nb_trial = 0; $nb_trial < $nb_trials; $nb_trial++){
    $a_trial_split = new Array
    $nb_tmp = $nb_trial
    for ($nb_digit = 0; $nb_digit < $nb_number; $nb_digit++){
      $a_trial_split[$nb_digit] = $nb_tmp - $nb_number * Math.floor($nb_tmp / $nb_number)
      $nb_tmp = Math.floor($nb_tmp / $nb_number)
    }
    for($i = 0; $i < $nb_number; $i++)
      for($j = 0; $j < $nb_number; $j++)
        if($i != $j)
          if($a_trial_split[$i] == $a_trial_split[$j])
            continue __trying__
    $nb_successes += 1
  }
  return $nb_successes
}

alert("input a number")
document.open()
document.write("<input type = text onblur = alert(factorial(parseInt(this.value))))>")
document.close()


Quỷ dữ:

  • Các loại ký hiệu tiếng Đức, sn_case sigils không cần thiết. Làm thế nào là xấu xa?
  • Phát minh ra quy ước của riêng tôi cho nhãn nhảy, không tương thích với việc sử dụng hiện tại của quy ước này.
  • Mỗi biến có thể là vô tình toàn cầu.
  • Giải pháp không phải O(n), không phải O(n!), mà là O(n^n). Điều này một mình sẽ có đủ điều kiện để đủ điều kiện ở đây.
  • Tăng một số và sau đó chuyển đổi thành cơ sở-n là một cách tồi để tạo danh sách các chuỗi. Ngay cả khi chúng tôi đã muốn trùng lặp. Phá vỡ bí ẩn cho n> 13 không phải là lý do duy nhất.
  • Tất nhiên chúng ta có thể đã sử dụng number.toString(base), nhưng nó không hoạt động cho các căn cứ trên 36. Vâng, tôi biết 36! là rất nhiều , nhưng vẫn ...
  • Tôi đã đề cập đến Javascript có toán tử mô đun chưa? Hay là Math.pow? Không? Ồ tốt
  • Từ chối sử dụng ++bên ngoài các vòng lặp làm cho nó thậm chí còn bí ẩn hơn. Ngoài ra, ==là xấu.
  • Cấu trúc vòng lặp lồng nhau sâu sắc lồng nhau. Ngoài ra, các điều kiện lồng nhau thay vì AND. Ngoài ra, điều kiện bên ngoài có thể tránh được bằng cách kết thúc vòng lặp bên trong tại$i .
  • Các chức năng new Array, document.write(với bạn bè) và alert(thay vì lời nhắc hoặc nhãn đầu vào) tạo thành một bộ ba hoàn chỉnh về tội lựa chọn chức năng. Tại sao đầu vào được thêm động?
  • Xử lý sự kiện nội tuyến. Oh, và đường ống sâu là địa ngục để gỡ lỗi.
  • Thuộc tính không được trích dẫn là thú vị và không gian xung quanh = khiến chúng khó đọc hơn.
  • Tôi đã đề cập đến tôi ghét dấu chấm phẩy?

8

Ruby và WolframAlpha

Giải pháp này sử dụng API REST của WolframAlpha để tính giai thừa, với RestClient để tìm nạp giải pháp và Nokogiri để phân tích cú pháp. Nó không phát minh lại bất kỳ bánh xe nào và sử dụng các công nghệ phổ biến và được thử nghiệm để có được kết quả theo cách hiện đại nhất có thể.

require 'rest-client'
require 'nokogiri'

n = gets.chomp.to_i
response = Nokogiri::XML(RestClient.get("http://api.wolframalpha.com/v2/query?input=#{n}!&format=moutput&appid=YOUR_APP_KEY"))
puts response.xpath("//*/moutput/text()").text

7

Javascript

Javascript là ngôn ngữ lập trình chức năng, điều này có nghĩa là bạn phải sử dụng các chức năng cho mọi thứ vì nó nhanh hơn.

function fac(n){
    var r = 1,
        a = Array.apply(null, Array(n)).map(Number.call, Number).map(function(n){r = r * (n + 1);});
    return r;
}

1
Bạn có thể giải thích?
Mhmd

7
1 không phải là một chức năng. Mã của bạn là do đó chậm.
Pierre Arlaud

4
@ArlaudPierre r = -~(function(){})chắc chắn sẽ giải quyết điều đó.
nitro2k01

4
Tôi đang ở trên một máy làm việc vì vậy tôi không thực sự muốn cài đặt ngôn ngữ này. Tôi có thể tìm phiên bản nào sẽ chạy trong trình duyệt của mình?
joeytwiddle

3
Tôi hơi sợ sử dụng Google vì sếp của tôi có tài khoản với họ và tôi không muốn anh ấy biết tôi đang chơi golf tại nơi làm việc. Tôi đang tìm kiếm một tiện ích mở rộng cho Firefox có thể chạy Javascript, nhưng dường như tôi không thể tìm thấy một tiện ích mở rộng. Một số bạn bè của tôi chạy Javascript trên jsfiddle.net nhưng đó là sử dụng điện của người khác giống như ăn cắp. Mẹ tôi nói tôi không nên quanh quẩn với những người như vậy, nhưng họ là bạn của tôi vậy tôi phải làm sao? Dù sao cô ấy đôi khi mất nhiều kem hơn cô ấy cần. Cảm ơn các mẹo, tôi sử dụng Ctrl-Shift-J hoặc K trong Firefox. Tuyên bố miễn trừ trách nhiệm: # bình luận-trolling
joeytwiddle

5

Sử dụng sắp xếp Bogo trong Java

public class Factorial {
    public static void main(String[] args) {
        //take the factorial of the integers from 0 to 7:
        for(int i = 0; i < 8; i++) {
            System.out.println(i + ": " + accurate_factorial(i));
        }
    }

    //takes the average over many tries
    public static long accurate_factorial(int n) {
        double sum = 0;
        for(int i = 0; i < 10000; i++) {
            sum += factorial(n);
        }
        return Math.round(sum / 10000);
    }

    public static long factorial(int n) {
        //n! = number of ways to sort n
        //bogo-sort has O(n!) time, a good approximation for n!
        //for best results, average over several passes

        //create the list {1, 2, ..., n}
        int[] list = new int[n];
        for(int i = 0; i < n; i++)
            list[i] = i;

        //mess up list once before we begin
        randomize(list);

        long guesses = 1;

        while(!isSorted(list)) {
            randomize(list);
            guesses++;
        }

        return guesses;
    }

    public static void randomize(int[] list) {
        for(int i = 0; i < list.length; i++) {
            int j = (int) (Math.random() * list.length);

            //super-efficient way of swapping 2 elements without temp variables
            if(i != j) {
                list[i] ^= list[j];
                list[j] ^= list[i];
                list[i] ^= list[j];
            }
        }
    }

    public static boolean isSorted(int[] list) {
        for(int i = 1; i < list.length; i++) {
            if(list[i - 1] > list[i])
                return false;
        }
        return true;
    }
}

Điều này thực sự hoạt động, chỉ rất chậm, và nó không chính xác cho những con số cao hơn.


4

PERL

Yếu tố có thể là một vấn đề khó khăn. Kỹ thuật lập bản đồ / thu nhỏ - giống như Google sử dụng - có thể phân tách toán học bằng cách loại bỏ một loạt các quy trình và thu thập kết quả. Điều này sẽ tận dụng tốt tất cả các lõi hoặc cpus trong hệ thống của bạn vào một đêm mùa đông lạnh.

Lưu dưới dạng f.perl và chmod 755 để đảm bảo bạn có thể chạy nó. Bạn đã cài đặt Lister rác rưởi bệnh hoạn chưa?

#!/usr/bin/perl -w                                                              
use strict;
use bigint;
die "usage: f.perl N (outputs N!)" unless ($ARGV[0] > 1);
print STDOUT &main::rangeProduct(1,$ARGV[0])."\n";
sub main::rangeProduct {
    my($l, $h) = @_;
    return $l    if ($l==$h);
    return $l*$h if ($l==($h-1));
    # arghhh - multiplying more than 2 numbers at a time is too much work       
    # find the midpoint and split the work up :-)                               
    my $m = int(($h+$l)/2);
    my $pid = open(my $KID, "-|");
      if ($pid){ # parent                                                       
        my $X = &main::rangeProduct($l,$m);
        my $Y = <$KID>;
        chomp($Y);
        close($KID);
        die "kid failed" unless defined $Y;
        return $X*$Y;
      } else {
        # kid                                                                   
        print STDOUT &main::rangeProduct($m+1,$h)."\n";
        exit(0);
    }
}

Quỷ dữ:

  • quy trình O (log2 (N))
  • không kiểm tra xem bạn có bao nhiêu CPU hoặc lõi
  • Ẩn nhiều chuyển đổi bigint / văn bản xảy ra trong mọi quy trình
  • Vòng lặp for thường nhanh hơn mã này

TIL rằng trong perl ARGV[0]thực sự là đối số đầu tiên và không phải là kịch bản!
ThinkChaos

@plg Tôi tin rằng $ 0 có thể chứa tên tệp kịch bản, nhưng nó không giống với $ ARGV [0]
Paul

Đúng, đó là những gì tôi đọc. Tôi chỉ thấy ngạc nhiên rằng trong perl không phải $ARGV[0]vì hầu hết các ngôn ngữ tôi biết một chút đều có nó
ThinkChaos

4

Con trăn

Chỉ cần một thuật toán O (n! * N ^ 2) để tìm giai thừa. Trường hợp cơ sở xử lý. Không tràn.

def divide(n,i):
    res=0
    while n>=i:
         res+=1
         n=n-i
    return res

def isdivisible(n,numbers):
    for i in numbers:
         if n%i!=0:
             return 0
         n=divide(n,i)
    return 1

def factorial(n):
    res = 1
    if n==0: return 1 #Handling the base case
    while not isdivisible(res,range(1,n+1)):
         res+=1
    return res

3

Vâng, có một giải pháp dễ dàng trong Golfscript. Bạn có thể sử dụng trình thông dịch Golfscript và chạy mã này:

.!+,1\{)}%{*}/

Dễ dàng nhỉ :) Chúc may mắn!


2
Tôi không biết GolfScript, nhưng điều này làm tôi thất vọng ... Dựa trên các ví dụ khác về GolfScript trên trang web này, tôi đã mong đợi câu trả lời sẽ là!
Mr Lister

1
Đó là toán tử phủ định. 0 trở thành 1 và mọi thứ khác trở thành 0.
Martijn Courteaux

3

Toán học

factorial[n_] := Length[Permutations[Table[k, {k, 1, n}]]]

Nó dường như không hoạt động đối với các số lớn hơn 11 và giai thừa [11] đóng băng máy tính của tôi.


3

Hồng ngọc

f=->(n) { return 1 if n.zero?; t=0; t+=1 until t/n == f[n-1]; t }

Một trong những lót chậm nhất tôi có thể tưởng tượng. Phải mất 2 phút trên bộ xử lý i7 để tính toán 6!.


2

Cách tiếp cận đúng cho các bài toán khó này là DSL. Vì vậy, tôi sẽ mô hình hóa điều này theo ngôn ngữ đơn giản

data DSL b a = Var x (b -> a)
             | Mult DSL DSL (b -> a)
             | Plus DSL DSL (b -> a)
             | Const Integer (b -> a) 

Để viết DSL của chúng tôi một cách độc đáo, thật hữu ích khi xem nó như một đơn nguyên miễn phí được tạo bởi functor đại số

F X = X + F (DSL b (F X)) -- Informally define + to be the disjoint sum of two sets

Chúng ta có thể viết điều này trong Haskell như

Free b a = Pure a
         | Free (DSL b (Free b a))

Tôi sẽ để nó cho người đọc rút ra cách thực hiện tầm thường của

join   :: Free b (Free b a) -> Free b a
return :: a -> Free b a
liftF  :: DSL b a -> Free b a

Bây giờ chúng ta có thể hủy bỏ một hoạt động để mô hình một giai thừa trong DSL này

factorial :: Integer -> Free Integer Integer
factorial 0 = liftF $ Const 1 id
factorial n = do
  fact' <- factorial (n - 1)
  liftF $ Mult fact' n id

Bây giờ chúng ta đã mô hình hóa điều này, chúng ta chỉ cần cung cấp một chức năng diễn giải thực tế cho đơn nguyên miễn phí của chúng ta.

denote :: Free Integer Integer -> Integer
denote (Pure a) = a
denote (Free (Const 0 rest)) = denote $ rest 0
...

Và tôi sẽ để phần còn lại của ký hiệu cho người đọc.

Để cải thiện khả năng đọc, đôi khi hữu ích khi trình bày AST cụ thể của biểu mẫu

data AST = ConstE Integer
         | PlusE AST AST
         | MultE AST AST

và sau đó là một sự phản ánh tầm thường

reify :: Free b Integer -> AST

và sau đó đơn giản để đánh giá đệ quy AST.


2

Con trăn

Dưới đây là phiên bản Python của giải pháp, không giới hạn ở giới hạn 32 bit (hoặc 64 bit trên hệ thống rất gần đây) đối với số nguyên trong Python. Để vượt qua giới hạn này, chúng ta sẽ sử dụng một chuỗi làm đầu vào và đầu ra cho factorialthường trình và chia chuỗi bên trong các chữ số của nó để có thể thực hiện phép nhân.

Vì vậy, đây là mã: getDigitshàm chia một chuỗi biểu thị một số thành các chữ số của nó, do đó "1234" trở thành [ 4, 3, 2, 1 ](thứ tự ngược lại chỉ làm cho hàm increasemultiplyhàm đơn giản hơn). Cácincrease chức năng có danh sách một ví dụ và tăng nó bằng một. Như tên cho thấy, multiplyhàm nhân lên, ví dụ multiply([2, 1], [3])trả về[ 6, 3 ] vì 12 lần 3 là 36. Điều này hoạt động theo cách tương tự như bạn sẽ nhân một cái gì đó bằng bút và giấy.

Cuối cùng, factorialhàm sử dụng các hàm trợ giúp này để tính giai thừa thực tế, ví dụ factorial("9")cho"362880" là sản lượng của nó.

import copy

def getDigits(n):
    digits = []
    for c in n:
        digits.append(ord(c) - ord('0'))

    digits.reverse()
    return digits

def increase(d):
    d[0] += 1
    i = 0
    while d[i] >= 10:
        if i == len(d)-1:
            d.append(0)

        d[i] -= 10
        d[i+1] += 1
        i += 1

def multiply(a, b):
    subs = [ ]
    s0 = [ ]
    for bi in b:

        s = copy.copy(s0)
        carry = 0
        for ai in a:
            m = ai * bi + carry
            s.append(m%10)
            carry = m//10

        if carry != 0:
            s.append(carry)

        subs.append(s)
        s0.append(0)

    done = False
    res = [ ]
    termsum = 0
    pos = 0
    while not done:
        found = False
        for s in subs:
            if pos < len(s):
                found = True
                termsum += s[pos]

        if not found:
            if termsum != 0:
                res.append(termsum%10)
                termsum = termsum//10
            done = True
        else:
            res.append(termsum%10)
            termsum = termsum//10
            pos += 1

    while termsum != 0:
        res.append(termsum%10)
        termsum = termsum//10

    return res

def factorial(x):
    if x.strip() == "0" or x.strip() == "1":
        return "1"

    factorial = [ 1 ]
    done = False
    number = [ 1 ]
    stopNumber = getDigits(x)
    while not done:
        if number == stopNumber:
            done = True

        factorial = multiply(factorial, number)
        increase(number)

    factorial.reverse()

    result = ""
    for c in factorial:
        result += chr(c + ord('0'))

    return result

print factorial("9")

Ghi chú

Trong python, một số nguyên không có giới hạn, vì vậy nếu bạn muốn làm điều này bằng tay, bạn có thể làm

fac = 1
for i in range(2,n+1): 
    fac *= i

Ngoài ra còn rất thuận tiện. math.factorial(n) chức năng .

Giải pháp này rõ ràng phức tạp hơn nhiều so với yêu cầu, nhưng nó thực sự hiệu quả và thực tế nó minh họa cách bạn có thể tính giai thừa trong trường hợp bạn bị giới hạn bởi 32 hoặc 64 bit. Vì vậy, trong khi không ai tin rằng đây là giải pháp bạn đưa ra cho vấn đề đơn giản này (ít nhất là trong Python), bạn thực sự có thể học được điều gì đó.


Không có giới hạn về số nguyên trong Python ... phải không? Bạn có thể cần phải giải thích điều này tốt hơn.
Đi xe đạp

@Riking Có, trong python không có giới hạn cho số nguyên. Tôi đã thêm một vài ghi chú để làm cho nó rõ ràng hơn.
brm 30/12/13

2

Con trăn

Giải pháp hợp lý nhất là rõ ràng kiểm tra tất cả các số cho đến khi bạn tìm thấy số đó là giai thừa của số đã cho.

print('Enter the number')
n=int(input())
x=1
while True:
    x+=1
    tempx=int(str(x))
    d=True
    for i in range(1, n+1):
        if tempx/i!=round(tempx/i):
            d=False
        else:
            tempx/=i
    if d:
        print(x)
        break

2

Một giải pháp đệ quy thanh lịch nhất trong C

Mọi người đều biết các giải pháp tao nhã nhất cho giai thừa là đệ quy.

Yếu tố:

0! = 1
1! = 1
n! = n * (n - 1)!

Nhưng phép nhân cũng có thể được định nghĩa đệ quy là bổ sung liên tiếp.

Phép nhân:

n * 0 = 0
n * 1 = n
n * m = n + n * (m - 1)

Và như vậy có thể bổ sung như gia tăng liên tiếp.

Thêm vào:

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

Trong C, chúng ta có thể sử dụng ++x--xđể xử lý các nguyên thủy (x + 1)(x - 1)tương ứng, vì vậy chúng ta có mọi thứ được xác định.

#include <stdlib.h>
#include <stdio.h>

// For more elegance, use T for the type
typedef unsigned long T;

// For even more elegance, functions are small enough to fit on one line

// Addition
T A(T n, T m) { return (m > 0)? A(++n, --m) : n; }

// Multiplication
T M(T n, T m) { return (m > 1)? A(n, M(n, --m)): (m? n: 0); }

// Factorial
T F(T n) { T m = n; return (m > 1)? M(n, F(--m)): 1; }

int main(int argc, char **argv)
{
    if (argc != 2)
        return 1;

    printf("%lu\n", F(atol(argv[1])));

    return 0;
}

Hãy thử xem:

$ ./factorial 0
1
$ ./factorial 1
1
$ ./factorial 2
2
$ ./factorial 3
6
$ ./factorial 4
24
$ ./factorial 5
120
$ ./factorial 6
720
$ ./factorial 7
5040
$ ./factorial 8
40320

Hoàn hảo, mặc dù 8! mất một thời gian dài vì một số lý do. Ồ, những giải pháp tao nhã nhất không phải lúc nào cũng nhanh nhất. Tiếp tục đi:

$ ./factorial 9

Hmm, tôi sẽ cho bạn biết khi nào nó quay lại ...


2

Con trăn

Như câu trả lời của @ Matt_Sieker đã chỉ ra, các yếu tố có thể được chia thành bổ sung - tại sao, chia nhỏ các nhiệm vụ là bản chất của lập trình. Nhưng, chúng ta có thể chia nó thành 1!

def complicatedfactorial(n):
    def addby1(num):
        return num + 1
    def addnumbers(a,b):
        copy = b
        cp2 = a
        while b != 0:
            cp2 = addby1(cp2)
            b -= 1
    def multiply(a,b):
        copy = b
        cp2 = a
        while b != 0:
            cp2 = addnumbers(cp2,cp2)
    if n == 0:
        return 1
    else:
        return multiply(complicatedfactorial(n-1),n)

Tôi nghĩ mã này đảm bảo Lỗi SO, bởi vì

  1. Đệ quy- làm nóng nó lên

  2. Mỗi lớp tạo ra các cuộc gọi để nhân lên

  3. tạo ra các cuộc gọi đến người nghiện

  4. tạo ra các cuộc gọi đến addby1!

Quá nhiều chức năng, phải không?



1

TI-Basic 84

:yumtcInputdrtb@gmail And:cReturnbunchojunk@Yahoo A!op:sEnd:theemailaddressIS Crazy ANSWER LOL

Nó thật sự có hiệu quả :)


1

Javascript

Rõ ràng công việc của một lập trình viên là làm càng ít công việc càng tốt, và sử dụng càng nhiều thư viện càng tốt. Do đó, chúng tôi muốn nhập jQuery math.js . Bây giờ, nhiệm vụ đơn giản như thế này:

$.alert=function(message){
    alert(message);
}$.factorial=function(number){
    alert(math.eval(number+"!"));
    return math.eval(number+"!");
}
$.factorial(10);

1

Con trăn

Chỉ với một sửa đổi nhỏ của việc thực hiện giai đoạn đệ quy tiêu chuẩn, nó trở nên chậm chạp không thể chấp nhận được đối với n> 10.

def factorial(n):
    if n in (0, 1):
        return 1
    else:
        result = 0
        for i in range(n):
            result += factorial(n - 1)
        return result

1

Bash

#! /bin/bash

function fact {
    if [[ ${1} -le 1 ]]; then
        return 1
    fi;

    fact $((${1} - 1))
    START=$(date +%s)
    for i in $(seq 1 $?); do sleep ${1}; done
    END=$(date +%s)
    RESULT=$(($END - $START))
    return $RESULT
}

fact ${1}
echo $?

1

Hãy thử làm theo Phương pháp Monte Carlo . Chúng ta đều biết rằng xác suất của hai n -permutations ngẫu nhiên bằng nhau là chính xác 1 / n! . Do đó, chúng tôi chỉ có thể kiểm tra có bao nhiêu bài kiểm tra cần thiết (hãy gọi số này là b ) cho đến khi chúng tôi nhận được c lượt truy cập. Sau đó, n! ~ b / c .

Sage, cũng nên làm việc với Python

def RandomPermutation(n) :           
    t = range(0,n)                   
    for i in xrange(n-1,0,-1):       
        x = t[i]                     
        r = randint(0,i)             
        t[i] = t[r]                  
        t[r] = x                     
    return t                         

def MonteCarloFactorial(n,c) :   
    a = 0                            
    b = 0                            
    t = RandomPermutation(n)         
    while a < c :                
        t2 = list(t)                 
        t = RandomPermutation(n)     
        if t == t2 :                 
            a += 1                   
        b += 1                       
    return round(b/c)            

MonteCarloFactorial(5,1000)
# returns an estimate of 5!

1

bash

Yếu tố dễ dàng được xác định với các công cụ dòng lệnh nổi tiếng từ bash.

read -p "Enter number: " $n
seq 1 $n | xargs echo | tr ' ' '*' | bc

Như @Aaron Davies đã đề cập trong các bình luận, điều này có vẻ gọn gàng hơn nhiều và tất cả chúng ta đều muốn có một chương trình hay và gọn gàng, phải không?

read -p "Enter number: " $n
seq 1 $n | paste -sd\* | bc

1
tôi đề nghị pastelệnh được đánh giá thấp :seq 1 $n | paste -sd\* | bc
Aaron Davies

2
@AaronDavies pastetrông giống như một từ tiếng Anh thông thường và dễ nhớ. Chúng ta có thực sự muốn điều đó? ; o)
jippie
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.