На скільки можна швидко розмножитися?


12

З недавнім ударом Python , ось спроба показати сильні сторони Python. Ваше завдання полягає в тому, щоб написати програму, яка обчислює максимальну кількість фактів протягом 10 секунд.n

Ваш рахунок буде (highest n for your program on your machine)/(highest n for my program on your machine)

Правила

  • Ви повинні обчислити точне ціле рішення. Оскільки факториал буде набагато вищим, ніж те, що може вміститися в 64-бітове цілочисене без знаку, ви можете використовувати рядки, якщо ваша мова не підтримує великі цілі числа
  • Стандартні лазівки заборонені. Зокрема, ви не можете використовувати будь-які зовнішні ресурси.
  • Тільки обчислювальна частина (сюди входить час для будь-яких обхідних шляхів з використанням рядків) додає до загального часу, який в середньому повинен становити менше 10 секунд.
  • Тільки однопотокові програми.
  • Ви повинні зберігати вихід у легко друкованій формі (оскільки друк вимагає часу) (див. Мою програму нижче), рядкові, змінні, масив символів тощо.

Редагувати:

  • Ваша програма повинна дати правильний вихід для всіх n:1 <= n <= (your highest n)

EDIT2:

  • Я ненавиджу це чітко, але використання вбудованих функціональних функцій вашої мови підпадає під стандартні лазівки http://meta.codegolf.stackexchange.com/a/1078/8766 Вибачте, Mathematica та Sage

Моя програма

from __future__ import print_function
import time


def factorial( n ):
    return reduce( ( lambda x , y : x * y ) , xrange( 1 , n + 1 ) , 1 )

start = time.clock()
answer = factorial( 90000 )
end = time.clock()

print ( answer )
print ( "Time:" , end - start , "sec" )

Найвищий результат виграє. Для запису мій код може управляти n = 90000приблизно за 9.89секунди на Pentium 4 3,0 ГГц


EDIT: Можеш все , будь ласка , додати рахунок , а не тільки найвищий п . Просто найвища nсама по собі ніякого значення не має, оскільки це залежить від обладнання. Інакше неможливо мати об'єктивний критерій виграшу. anwer авеша робить це правильно.


У нас є переможець. Я не прийняв відповідь на Java /codegolf//a/26974/8766, оскільки це спідниці, близькі до http://meta.codegolf.stackexchange.com/a/1080/8766


1
Ви можете використовувати operator.mulзамість функції лямбда
gnibbler

1
Трохи здивований це працює, але якщо припустити, що я правильно прочитав правила, це рішення MATLAB було б непогано:, factorial(Inf)повертається Infза частку секунди.
Денніс Джахеруддін

1
@Doorknob Це вписується у стандартні лазівки.
Джастін

1
@DennisJaheruddin, це трохи розтягнути, щоб позначати "Inf" як "точне ціле рішення".
tobyink

1
@Quincunx Ні, будь-яка мова дозволена.
користувач80551

Відповіді:


7

C ++ з GMP, оцінка = 55,55 (10 000 000/180 000)

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <queue>
#include <gmpxx.h>

int main(int argc, char *argv[]) {
  uint64_t n = atoi(argv[1]);

  // Iterate through 1..n.  Strip off powers of 2.  Multiply
  // remainders together into <= 64 bit chunks.
  uint64_t twos = 0;
  std::vector<uint64_t> terms;
  uint64_t m = 1;
  for(uint64_t i = 1; i <= n; i++) {
    uint64_t j = __builtin_ctzll(i);
    twos += j;
    uint64_t k = i >> j;
    if(__builtin_clzll(m) + __builtin_clzll(k) >= 64) {
      m *= k;
    } else {
      terms.push_back(m);
      m = k;
    }
  }
  if(m != 1) terms.push_back(m);

  // convert to gmp
  // why isn't there a 64-bit constructor?
  std::queue<mpz_class> gmpterms;
  for(int i = 0; i < terms.size(); i++) {
    mpz_class x = (uint32_t)(terms[i] >> 32);
    x <<= 32;
    x += (uint32_t)terms[i];
    gmpterms.push(x);
  }

  // pop two from the bottom, multiply them, push on the end.
  while(gmpterms.size() > 1) {
    mpz_class a = gmpterms.front();
    gmpterms.pop();
    mpz_class b = gmpterms.front();
    gmpterms.pop();
    gmpterms.push(a * b);
  }

  mpz_class r = gmpterms.front();
  r <<= twos;
  //std::cout << r << std::endl;
}

8

Python 2.7

42,575 = (6,812,000 / 160,000) бл


Код:

import gmpy2

def fac1(n):
    m=lambda(L):([]if len(L)%2==0 else[L.pop()])+map(lambda(l):l[0]*l[1],zip(L[1::2],L[-2::-2]))
    L=map(gmpy2.mpz,xrange(1,n+1))
    Number = (len(L)-1).bit_length()
    while Number:Number-=1;L=m(L)
    return L[0]

def fac2(n):
    global E; E=0
    def f(i):
        global E; E+=i//2
        return[]if i==1 else f(i//2)+range(3,i,2)+[[1,i][i%2]]
    m=lambda(L):([]if len(L)%2==0 else[L.pop()])+map(lambda(l):l[0]*l[1],zip(L[1::2],L[-2::-2]))
    L=map(gmpy2.mpz,f(n))
    N=(len(L)-1).bit_length()
    while N: N-=1;L=m(L)
    return L[0]<<E

Тест:

import time

start = time.time()
baseline(160000)
print time.time()-start

start = time.time()
fac1(6811000)
print time.time()-start

start = time.time()
fac2(6812000)
print time.time()-start

start = time.time()
gmpy2.fac(26000000)
print time.time()-start

Вихід:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

Як це працює:

Більші множення займають більше часу, тому ми хочемо зробити якомога більше малих множин. Особливо це стосується Python, де для чисел менше, ніж 2^64ми використовуємо апаратну арифметику, і вище, ніж ми використовуємо програмне забезпечення. Отже, в m(L), ми починаємо зі списку L; якщо це непарна довжина, ми видаляємо одне число з розгляду, щоб зробити його ще раз. Потім ми множимо елемент 1на елемент -2, елемент 3на -4і т.д., так що

m([1,2,3,4,5,6,7,8]) = [2*7, 4*5, 6*3, 8*1] = [14, 20, 18, 8]
m([10,12,6]) = [360,112]
m([120,6]) = [40320]

Цей підхід гарантує, що ми використовуємо апаратну арифметику якомога довше, після чого ми переходимо на ефективну арифметичну бібліотеку GMC.

В fac2, ми візьмемо більш класичний розділяй і володарюй підхід, а також , де ми розбиваємо кожну кратні 2 і Bitshift їх в кінці для тривіального збільшення продуктивності. Я включив його сюди, тому що це зазвичай на 0,5% швидше, ніж fac1.

Версія для гольфу fac1(тому що я можу), 220B

import gmpy2
def f(n):
    m=lambda(L):([]if len(L)%2==0 else[L.pop()])+map(lambda(l):l[0]*l[1],zip(L[1::2],L[-2::-2]))
    L=map(gmpy2.mpz,xrange(1,n+1));N=(len(L)-1).bit_length()
    while N:N-=1;L=m(L)
return L[0]

1
Якщо підсистема GMP включає функцію бітшіффіт, то ви можете зберегти числа ще меншими, розділивши кожне число у списку на 2, поки воно не стане рівним, а потім зробіть одну зміну в кінці.
Пітер Тейлор

Звідки ти взявся gmpy2? $ python Python 2.7.3 (за замовчуванням, 27 лютого 2014, 19:58:35) [GCC 4.6.3] в linux2 Введіть "довідка", "авторські права", "кредити" або "ліцензія" для отримання додаткової інформації. >>> з gmpy2 import mpz Traceback (останній дзвінок останній): Файл "<stdin>", рядок 1, в <module> ImportError: Немає модуля з іменем gmpy2 >>>
користувач80551

@ user80551: code.google.com/p/gmpy (верхній результат пошуку в Google) має інсталяторів для багатьох різних платформ.
Олександр-Бретт

Для версії для гольфу ви не могли зробити це while len(L): ...замість цього while len(L)>1: ...?
user80551

Ні: функція всередині цього циклу ніколи не прийме список нижче довжини 1, і все одно нам потрібен перший елемент!
Олександр-Бретт

2

Ява - 125,15 (21 400 000/171 000)

Крім того, що безсоромно скопійовано з Github repo Пітера Лушного (спасибі @ напівкрединне) та отримало ліцензію за ліцензією MIT, цей алгоритм "просте факторизування вкладених квадратиків", запропонований Альбертом Шьонгаге та ін. (відповідно до сторінки опису факторіальних алгоритмів Лушни ).

Я трохи адаптував алгоритм для використання BigInteger Java та не використовувати таблиці пошуку для n <20.

Скомпільовано з gcj, який використовує GMP для своєї реалізації BigInteger і працює на Linux 3.12.4 (Gentoo), на Core i7 4700MQ на частоті 2,40 ГГц

import java.math.BigInteger;

public class PrimeSieveFactorialSchoenhage {

    private static int[] primeList, multiList;

    public static BigInteger factorial(int n) {
        int log2n = 31 - Integer.numberOfLeadingZeros(n);
        int piN = log2n < 2 ? 1 : 2 + (15 * n) / (8 * (log2n - 1));

        primeList = new int[piN];
        multiList = new int[piN];

        int len = primeFactors(n);
        return nestedSquare(len).shiftLeft(n - Integer.bitCount(n));
    }

    private static BigInteger nestedSquare(int len) {
        if (len == 0) {
            return BigInteger.ONE;
        }

        int i = 0, mult = multiList[0];

        while (mult > 1) {
            if ((mult & 1) == 1) { // is mult odd ?
                primeList[len++] = primeList[i];
            }

            multiList[i++] = mult / 2;
            mult = multiList[i];
        }
        BigInteger ns = nestedSquare(i);
        if (len <= i) {
            return ns.multiply(ns);
        }

        return product(primeList, i, len - i).multiply(ns.multiply(ns));
    }

    private static BigInteger product(int[] a, int start, int length) {
        if (length == 0) {
            return BigInteger.ONE;
        }

        int len = (length + 1) / 2;
        long[] b = new long[len];

        int i, j, k;

        for (k = 0, i = start, j = start + length - 1; i < j; i++, k++, j--) {
            b[k] = a[i] * (long) a[j];
        }

        if (i == j) {
            b[k++] = a[j];
        }

        return recProduct(b, 0, k - 1);
    }

    private static BigInteger recProduct(long[] s, int n, int m) {
        if (n > m) {
            return BigInteger.ONE;
        }
        if (n == m) {
            return BigInteger.valueOf(s[n]);
        }
        int k = (n + m) >> 1;
        return recProduct(s, n, k).multiply(recProduct(s, k + 1, m));
    }

    private static int primeFactors(int n) {
        int[] primes = new int[n < 17 ? 6 : (int) Math.floor(n / (Math.log(n) - 1.5))];
        int numPrimes = makePrimeList(n, primes);

        int maxBound = n / 2, count = 0;

        int start = indexOf(primes, 2, 0, numPrimes - 1);
        int end = indexOf(primes, n, start, numPrimes);

        for (int i = start; i < end; i++) {
            int prime = primes[i];
            int m = prime > maxBound ? 1 : 0;

            if (prime <= maxBound) {
                int q = n;
                while (q >= prime) {
                    m += q /= prime;
                }
            }

            primeList[count] = prime;
            multiList[count++] = m;
        }
        return count;
    }

    private static int indexOf(final int[] data, int value, int low, int high) {
        while (low < high) {
            int mid = (low + high) >>> 1;

            if (data[mid] < value) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }

        if (low >= data.length) {
            return low;
        }

        if (data[low] == value) {
            low++;
        }

        return low;
    }

    private static int makePrimeList(int n, int[] prime) {
        boolean[] composite = new boolean[n / 3];

        sieveOfEratosthenes(composite);

        boolean toggle = false;
        int p = 5, i = 0, j = 2;

        prime[0] = 2;
        prime[1] = 3;

        while (p <= n) {
            if (!composite[i++]) {
                prime[j++] = p;
            }
            // -- never mind, it's ok.
            p += (toggle = !toggle) ? 2 : 4;
        }

        return j; // number of primes
    }

    private static void sieveOfEratosthenes(final boolean[] composite) {
        int d1 = 8;
        int d2 = 8;
        int p1 = 3;
        int p2 = 7;
        int s1 = 7;
        int s2 = 3;
        int n = 0;
        int len = composite.length;
        boolean toggle = false;

        while (s1 < len) { // -- scan sieve
            if (!composite[n++]) { // -- if a prime is found, cancel its multiples
                int inc = p1 + p2;

                for (int k = s1; k < len; k += inc) {
                    composite[k] = true;
                }

                for (int k = s1 + s2; k < len; k += inc) {
                    composite[k] = true;
                }
            }

            if (toggle = !toggle) { // Never mind, it's ok.
                s1 += d2;
                d1 += 16;
                p1 += 2;
                p2 += 2;
                s2 = p2;
            } else {
                s1 += d1;
                d2 += 8;
                p1 += 2;
                p2 += 6;
                s2 = p1;
            }
        }
    }

    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        long nanos = System.nanoTime();
        BigInteger fact = factorial(n);
        nanos = System.nanoTime() - nanos;
        // Commented out because it takes ages to print
        //System.out.println(fact);
        System.out.println(nanos / 1e9);
    }
}

Складено зgcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r

1

Пітон 3, n = 100000

Проста зміна алгоритму - це все, що потрібно, щоб збільшити зразок коду до 10000.

import time

def factorial(n):
    result = 1
    while n > 0:
        result *= n
        n = n - 1
    return result

start = time.clock()
answer = factorial(100000)
end = time.clock()

print(answer)
print("Time:", end - start, "sec")

Очевидно, це не найкреативніша відповідь, але насправді існує лише один спосіб зробити факторіал….


Будь ласка, дайте рахунок, дивіться мою редакцію. Опуклість , ймовірно , буде , тому що ваша машина краще , ніж у мене.
користувач80551

1

Perl + C, n = близько 3 млн

Тут я використовую бібліотеку Math :: BigInt :: GMP, доступну в CPAN, яка забезпечує масований приріст швидкості для основних ядерних об'єктів Math :: BigInt.

use v5.14;
use Time::HiRes 'time';
use Math::BigInt only => 'GMP';

sub factorial { Math::BigInt::->new(@_)->bfac }

my $start  = time;
my $answer = factorial( 3_000_000 );
my $end    = time;

say $answer;
say "Time: ", $end - $start, " sec";

Майте на увазі, що мій комп'ютер, ймовірно, трохи повільніше, ніж ваш. Використовуючи ваш оригінальний сценарій Python, я можу розрахувати лише factorial(40000)за 10 секунд; factorial(90000)займає набагато більше часу. (Я натискаю Ctrl + C через хвилину.) На вашому обладнання, використовуючи Math :: BigInt :: GMP, ви, можливо, зможете обчислити коефіцієнт 5 мільйонів і більше за 10 секунд.

Одне, що ви можете помітити, це те, що, хоча фабрика обчислюється неймовірно швидко, результат друкується дуже повільно, займаючи приблизно втричі довше, ніж початковий розрахунок. Це пояснюється тим, що GMP використовує внутрішнє двійкове, а не десяткове представлення, а для його друку потрібно двійкове перетворення у десяткове.


1
Я думаю, що GMP вважається зовнішнім ресурсом. (Хоча це, безумовно, робить набагато простіше, ніж реалізовувати основну факторизацію та множення Шенхаге-Страссена з нуля.)
r3mainer

3
Я припускав, що "зовнішній ресурс" стосується пошуку рішень із заздалегідь обчисленого набору відповідей у ​​базі даних або веб-сервісу тощо.
Tobyink

Схильний: бібліотеки зазвичай не вважаються зовнішніми ресурсами, якщо вони не мають функції, яка підпадає під правило нудної лазівки.
Олександр-Бретт

1
Tobyink: чи можете ви пояснити, що робить ваша програма? схоже, ви просто використовуєте вбудовану функцію (bfac?)
alexander-brett

Так. Ця відповідь є недійсною, оскільки використовується факторний методMath::BigInt
14mRh4X0r

1

Python 2.7
5.94 = 1'200'000 / 202'000

def fast_fac(n):
    def prod(start, fin):
            if fin - start <= 50:
                    return reduce(lambda x,y: x*y, xrange(start, fin+1), 1)
            else:
                    mid = (start+fin) / 2
                    return prod(start, mid) * prod(mid+1, fin)
    return prod(1, n)

Використовує відносну простоту множення багатьох груп малих чисел, а потім їх множення порівняно з великою кількістю множення, що включає величезне число.


1

C #: 0,48 (77 000/160 000)

Я не задоволений цим.

Це C # повільно?

Але ось все-таки мій запис.

static void Main(string[] args)
    {
        Console.WriteLine("Enter N for fatorial:");
        int n = Convert.ToInt32(Console.ReadLine());

        Stopwatch s = Stopwatch.StartNew();


        BigInteger result = 1;
        while (0 <-- n) result *= n;

        s.Stop();

        Console.WriteLine("Output: {0} ", result);

        Console.WriteLine("Completed in {0}", s.Elapsed);

    }

Коли n = 77000, потрібно 00:00:09:8708952обчислити.

Я працюю в режимі випуску за межами Visual Studio, використовуючи Core i3-2330M @ 2.2GHz.

Редагувати: Оскільки я не роблю нічого розумного, я приймаю цей результат. Можливо .NET Framework 4.5 додає накладні витрати (або BigInteger не такий швидкий).


Будь ласка, дайте рахунок, а не лишеn
user80551

1
Ви можете скористатися zero approached byоператором, щоб зробити його красивішим (наприклад, почніть з n = ... + 1того, як зробити while (0 <-- n) result *= n;)
Cthulhu

1
BigInteger для .NET, ймовірно, не реалізував алгоритми множення більших чисел, як-от Карацуба або Тоом-3. Якщо так, це хороший приклад того, як Python швидший.
Керніг

1

bc, оцінка = 0,19

Що за чорт, ось мій претендент на "На скільки ти можеш повільно розмножуватися?"

bc - "Довільна мова калькулятора точності", але, на жаль, досить повільна:

n=read()
for(f=i=1;i<=n;i++)f*=i
f
quit

Приблизно за 10 секунд в моєму середині 2012 року MacBook Pro (2.3 ГГц Intel Core i7) відповідь відповіді python може обчислити 122000 !, але цей сценарій bc може обчислити лише 23600 !.

І навпаки 10000! займає 1,5 секунди з довідковим сценарієм python, але сценарій bc займає 50s.

О Боже.


1
OpenBSD bc (1) швидше. Ваша програма балів 0,29 = 28000/98000. Немає read(), тому я побіг time sed 's/read()/28000/' factorial.bc | bc.
Керніг

1

Bash: оцінка = 0,001206 (181/150000)

Я вкрав математичні функції з Rosettacode - Довге множення я не аналізував і не намагався оптимізувати.
Ви можете змінити алгоритм або спробувати інший метод розділення рядків.

#!/bin/bash


add() { # arbitrary-precision addition
  if (( ${#1} < ${#2} )); then
    local a="$2" b="$1" sum= carry=0
  else
    local a="$1" b="$2" sum= carry=0
  fi

  while (( ${#a} )); do
    local -i d1="${a##${a%?}}" d2="10#0${b##${b%?}}" s=carry+d1+d2
    sum="${s##${s%?}}$sum"
    carry="10#0${s%?}"
    a="${a%?}" b="${b%?}"
  done
  echo "$sum"
}

multiply() { # arbitrary-precision multiplication
  if (( ${#1} < ${#2} )); then
    local a="$2" b="$1" product=0
  else
    local a="$1" b="$2" product=0
  fi

  local zeroes=
  while (( ${#b} )); do
    local m1="$a"
    local m2="${b##${b%?}}"
    local partial=$zeroes 
    local -i carry=0
    while (( ${#m1} )); do 
      local -i d="${m1##${m1%?}}"
      m1="${m1%?}"
      local -i p=d*m2+carry
      partial="${p##${p%?}}$partial"
      carry="10#0${p%?}"
    done
    partial="${carry#0}$partial"
    product="$(add "$product" "$partial")"
    zeroes=0$zeroes
    b="${b%?}"
  done
  echo "$product"
}

# 'timerun' function
trap 'echo $((i -1)) $f; exit'  USR1  
(sleep 9.9; kill -USR1 $$)&

declare -i i 
f=1
for ((i=1; i< 10000 ; i++ ))   # 10000 is verry optimistic
do
    f=$(multiply $f $i)
done 

1
Будь ласка, додайте бал, а не лише найвищий n
user80551

@ user80551 Зроблено
Еммануель

1

Python 3, вдосконалений альго Петра Лучного: 8,25x (1 280 000/155 000)

Безсоромно копіюється від Петра Лучного,
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm ,
який надає цей код за ліцензією "Creative Commons Attribution-ShareAlike 3.0".

Це насправді досить просунутий алгоритм, що використовує щось, що називається "розгойдується факторіал" і список простих. Я підозрюю, що це може бути ще швидше, якби він подобався багатьом з інших відповідей і виконував більшість множень з 32 бітовими цілими числами.

#! /usr/bin/python3
import time
import bisect 

def Primes(n) : 
  primes = [2, 3] 
  lim, tog = n // 3, False 
  composite = [False for i in range(lim)] 

  d1 = 8; d2 = 8; p1 = 3; p2 = 7; s = 7; s2 = 3; m = -1 

  while s < lim :             # --  scan the sieve 
      m += 1                  # --  if a prime is found 
      if not composite[m] :   # --  cancel its multiples 
          inc = p1 + p2 
          for k in range(s,      lim, inc) : composite[k] = True 
          for k in range(s + s2, lim, inc) : composite[k] = True 

          tog = not tog 
          if tog: s += d2; d1 += 16; p1 += 2; p2 += 2; s2 = p2 
          else:   s += d1; d2 +=  8; p1 += 2; p2 += 6; s2 = p1 

  k, p, tog = 0, 5, False 
  while p <= n : 
      if not composite[k] : primes.append(p) 
      k += 1; 
      tog = not tog 
      p += 2 if tog else 4 

  return primes 

def isqrt(x): 
  ''' 
  Writing your own square root function
  ''' 
  if x < 0: raise ValueError('square root not defined for negative numbers') 
  n = int(x) 
  if n == 0: return 0 
  a, b = divmod(n.bit_length(), 2) 
  x = 2**(a + b) 
  while True: 
      y = (x + n // x) // 2 
      if y >= x: return x 
      x = y 

def product(s, n, m): 
  if n > m: return 1 
  if n == m: return s[n] 
  k = (n + m) // 2 
  return product(s, n, k) * product(s, k + 1, m) 

def factorialPS(n): 

  small_swing = [1,1,1,3,3,15,5,35,35,315,63,693,231,3003,429,6435,6435, 
          109395,12155,230945,46189,969969,88179,2028117,676039,16900975, 
          1300075,35102025,5014575,145422675,9694845,300540195,300540195] 

  def swing(m, primes): 
      if m < 33: return small_swing[m] 

      s = bisect.bisect_left(primes, 1 + isqrt(m)) 
      d = bisect.bisect_left(primes, 1 + m // 3) 
      e = bisect.bisect_left(primes, 1 + m // 2) 
      g = bisect.bisect_left(primes, 1 + m) 

      factors = primes[e:g] 
      factors += filter(lambda x: (m // x) & 1 == 1, primes[s:d]) 
      for prime in primes[1:s]:   
          p, q = 1, m 
          while True: 
              q //= prime 
              if q == 0: break 
              if q & 1 == 1: 
                  p *= prime 
          if p > 1: factors.append(p) 

      return product(factors, 0, len(factors) - 1) 

  def odd_factorial(n, primes): 
      if n < 2: return 1 
      return (odd_factorial(n // 2, primes)**2) * swing(n, primes) 

  def eval(n): 
      if n < 0: 
          raise ValueError('factorial not defined for negative numbers') 

      if n == 0: return 1 
      if n < 20: return product(range(2, n + 1), 0, n-2) 

      N, bits = n, n 
      while N != 0: 
          bits -= N & 1 
          N >>= 1 

      primes = Primes(n) 
      return odd_factorial(n, primes) * 2**bits 

  return eval(n)

start = time.time()
answer = factorialPS(1280000) 
print(time.time()-start)

1

Ява - 10,9

n = 885000

Mergesort-y.

import java.math.BigInteger;

public class Factorials {

    public static BigInteger fac;

    public static BigInteger two = BigInteger.valueOf(2);

    static BigInteger mul(BigInteger start, BigInteger end) {
        if(start.equals(end)) {
            return start;
        } else {
            BigInteger mid = start.add(end.subtract(start).divide(Factorials.two));
            return Factorials.mul(start, mid).multiply(Factorials.mul(mid.add(BigInteger.ONE), end));
        }
    }

    public static void main(String[] args) {
        Factorials.fac = BigInteger.valueOf(Integer.parseInt(args[0]));
        long t = System.nanoTime();
        BigInteger result = mul(BigInteger.ONE, fac);
        t = System.nanoTime() - t;
        System.out.print(String.valueOf(((float) t) / 1000000000)); //result.toString()+" @ "+
    }
}

BigIntegers повільні.

Рекомендації для довільно-точних високошвидкісних цілочисельних бібліотек Java? : P


Чи можу я вкрасти ваш код, щоб зробити його багатопотоковим?
Саймон Куанг

@SimonKuang Вперед. : P Однак багатопотокові записи тут заборонені. Крім того, ви можете скористатися більш ефективною реалізацією BigInteger.
cjfaure

Mergesort-y Це називається розділити і перемогти.
johnchen902

1

C ++ (специфічно для x86_64) - 3,0 (390000/130000)

(легко переноситься на x86-32, перенесення на інші архітектури означає значну втрату швидкості)

Ось моя власна мікрореалізація тривалої арифметики.
Сам розрахунок займає 10 секунд, і, поки вихід знаходиться у легко друкуваному вигляді (див. operator<<Перевантаження), для його друку потрібно ще трохи часу.

#include <vector>
#include <iostream>
#include <stdint.h>
#include <ctime>

typedef uint64_t digit;
typedef std::vector<digit> number;

std::ostream &operator<<(std::ostream &s, const number &x)
{
    std::vector<char> o;
    size_t size = x.size() * 21;
    o.resize(size);
    size_t lud = 0;
    for(number::const_reverse_iterator i = x.rbegin(), end = x.rend(); i != end; i++)
    {
        digit carry = 0;
        int j;
        for(j = 0; j <= lud || carry; j++)
        {
            digit r = o[j] * (1LL << 32) + carry;
            o[j] = r % 10;
            carry = r / 10;
        }
        lud = j;
        carry = 0;
        for(j = 0; j <= lud || carry; j++)
        {
            digit r = o[j] * (1LL << 32) + carry;
            o[j] = r % 10;
            carry = r / 10;
        }
        lud = j;
        carry = *i;
        for(j = 0; carry; j++)
        {
            digit r = o[j] + (carry % 10);
            carry /= 10;
            carry += r / 10;
            o[j] = r % 10;
        }
        if(j > lud)
            lud = j;
    }
    for(int j = lud; j--;)
        s.put(o[j] + '0');
    return s;
}

inline uint64_t dmul(uint64_t x, uint64_t y, uint64_t &carry)
{
    asm("mulq %2" : "+a"(x), "=d"(carry) : "r"(y));
    return x;
}
inline digit dadd(digit x, digit y, digit &carry)
{
    asm("movq $0, %1; addq %2, %0; adcq %1, %1" : "+r"(x), "=r"(carry), "+r"(y));
    return x;
}

void multiply(number &x, digit y)
{
    x.resize(x.size() + 2);
    digit carry = 0;
    for(number::iterator i = x.begin(), end = x.end(); i != end; i++)
    {
        digit nc, res = dmul(*i, y, nc);
        *i = dadd(res, carry, carry);
        carry += nc;
    }
    size_t sz = x.size();
    for(number::const_reverse_iterator i = x.rbegin(), end = x.rend(); i != end; i++)
    {
        if(*i)
            break;
        sz--;
    }
    x.resize(sz);
}

int main()
{
    const int r = 390000;
    clock_t start = clock();
    number n;
    digit mult = 1;
    n.push_back(1);
    for(digit a = 2; a <= r; a++)
    {
        digit carry, m = dmul(mult, a, carry);
        if(carry)
        {
            multiply(n, mult);
            mult = a;
        }
        else
            mult = m;
    }
    multiply(n, mult);
    std::cout << "Took: " << (clock() - start)/((double)CLOCKS_PER_SEC) << std::endl;
    std::cout << n << std::endl;
}

Перевір свій рахунок. На комп'ютері потрібно запустити програму Python 2.7. Для свого комп’ютера я склав вашу програму, g++ -O2 factorial.cc -o factorialі вона набирає 3,90 = 382000 / 98000.
kernigh,

Дивно, я отримав 3,9, а ви отримали 3,0 для цієї програми. Я думаю, ваш швидший комп'ютер - це штраф. Можливо, ваша програма втрачає перевагу перед Python у міру rзбільшення. Якщо так, і ви можете зробити більше rза 10 секунд, то ваш рахунок знизиться.
Керніг

0

Пітон 3: 280000/168000

Час виконання вашої програми: між 9.87585953253та 10.3046453994. Час роботи моєї програми: о 10.35296977897559.

import time

def factorial(n):
    f = 1
    while n > 1:
        hn = n >> 1
        f = f * 2**hn * double_factorial(n) #dfl[hn + (n & 1) - 1]
        n = hn
    return f
def double_factorial(n):
    #dfl = [1]
    p = 1
    l = 3
    mh = n
    while l <= n:
        p *= l
        l += 2
        #dfl.append(p)
    return p

start = time.clock()
factorial(280000)
end = time.clock()

print(end - start)

Я прочитав цю відповідь на cs.SE і вирішив спробувати її реалізувати в Python. Однак я випадково виявив це n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(зауважте: !!це подвійна факторія ). Тому я перетворив це на нерекурсивну форму.

У коментарях показано мою спробу уникнути перерахунку подвійних факторіалів, але я виявив, що зберігання кожного значення було занадто дорогим у пам'яті, щоб змусити мій комп'ютер працювати ще повільніше. Я можу покращити це, зберігаючи лише те, що потрібно.

Як не дивно, я реалізував наївне пряме множення в Python 3, і це краще, ніж у вашій програмі: n = 169000за 10 секунд .:

def factorial(n):
    p=1
    for i in range(n):
        p*=i+1
    return p

0

Рубін 2.1

оцінка = 1,80 = 176_000 / 98_000

EDIT: покращено з 1,35 = 132_000 / 98_000

Я брав ідеї з факторного алгоритму GMP . Ця програма використовує стандартну бібліотеку для генерації простих чисел. Ruby - це поганий вибір, тому що множення здається повільніше у Ruby, ніж у Python.

  1. Моя програма в Ruby 2.1: оцінка = 1,80 = 176_000 / 98_000
  2. Тривіальний алгоритм в Python 2.7: оцінка = 1 = 98_000 / 98_000
  3. Trivial алгоритм в Рубіні 2.1: оцінка = 0,878 = 86_000 / 98_000

Так, мій бінарний ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]посилання проти GMP. Ruby 2.1 додав функцію використання GMP для великого множення, але це все ще здається повільніше, ніж Python 2.7.

require 'benchmark'
require 'optparse'
require 'prime'

def factorial(n)
  # calculate primes up to n, drop the 2
  @odd_primes = Prime.each(n).drop(1)

  # count prime factors of factorial(n)
  @factors = Hash.new(0)
  factorial_recurse(n)

  shift = @factors.delete(2) || 0
  @factors.inject(1) {|product, (base, exp)|
    product * base**exp
  } << shift
end

def factorial_recurse(n)
  return if n < 2

  # collect prime factors of 2 * 4 * 6 * .. * n
  #  = (2 * 2 * 2 * .. * 2) * (1 * 2 * 3 * .. * exp)
  #  = 2**exp * factorial(exp) where exp = floor(n/2)
  exp = n >> 1
  factorial_recurse(exp)
  @factors[2] += exp

  # collect prime factors 3 * 5 * 7 * ... * n
  for prime in @odd_primes
    break if prime > n
    exp = 0
    # count occurences of prime, prime**2, prime**3, .. n
    prime_power = prime
    until prime_power > n
      # floor(n / prime_power) occurences in 1 * 2 * .. * n,
      # but only ceil(count / 2) occurences in 3 * 5 * .. * n
      @factors[prime] += (n / prime_power + 1) >> 1
      prime_power *= prime
    end
  end
end

# usage: factorial.rb [-ct] [number]
cflag = tflag = false
OptionParser.new {|opts|
  opts.on('-c', 'Check for bugs') { cflag = true }
  opts.on('-t', 'Use trivial algorithm') { tflag = true }
  opts.parse!
}
$*[1] and fail 'too many arguments'
n = Integer($*[0] || 176_000)

if cflag
  factorial(n) == (1..n).reduce(1, :*) or
    fail "bad program: factorial(#{n}) is wrong"
  puts "ok"
  exit
end

# measure processor time to calculate factorial
f = nil
if tflag
  time = Benchmark.measure { f = (1..n).reduce(1, :*) }
else
  time = Benchmark.measure { f = factorial(n) }
end
puts f
puts "Time #{time.total} sec"

0

Юлія - ​​оцінка = 15.194

Використовуючи такий самий підхід, що і у довідковій програмі ... тобто

f(n)=reduce(*,1:big(n))

Таким чином, він використовує скорочення, основну операцію множення бінарних даних і діапазон (в цьому випадку, використовуючи велику (n), щоб примусити обчислення робити в BigInt, а не Int64). З цього я отримую

julia> @time K=f(2340000);
elapsed time: 9.991324093 seconds (814552840 bytes allocated)

На моєму комп’ютері, за допомогою довідкової програми, що працює з входом 154000, я отримую Time: 10.041181 secвихід (запустіть за допомогою python ./test.py, де test.py - це ім'я файлу, що містить довідковий код)


0

tcl, 13757

Моя відповідь - перевірити межі tcl.

Перший рядок полягає лише у встановленні вхідного параметра:

set n 13757

Інші - сам алгоритм:

set r 2
for {set i 3} {$i <= $n} {incr i} {set r [expr {$r*$i}]}   
puts $r

Я перевірив свій код на http://rextester.com/live/WEL36956 ; Якщо я роблю n більшим, я отримую SIGKILL; не можу отримати більше на локальному перекладачі tcl, якого у мене немає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.