Сколько можно быстро умножить?

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 ГГц


РЕДАКТИРОВАТЬ: все могут, пожалуйста, добавить счет, а не только самый высокий п . Просто высшее nне имеет значения само по себе, так как оно зависит от вашего оборудования. Иначе невозможно иметь объективный критерий победы. ani0sha anwer делает это правильно.


У нас есть победитель. Я не принял java-ответ /codegolf//a/26974/8766, так как он похож на юбки, близкие к http://meta.codegolf.stackexchange.com/a/1080/8766

user80551
источник
1
Вы можете использовать operator.mulвместо лямбда-функции
gnibbler
1
Немного удивило, что это работает, но при условии, что я правильно прочитал правила, это решение MATLAB было бы неплохо: factorial(Inf)возвращает Infв доли секунды.
Деннис Джаэруддин
1
@ Doorknob Это вписывается в стандартные лазейки.
Джастин
1
@DennisJaheruddin, называть «Inf» «точным целочисленным решением» довольно сложно.
tobyink
1
@Quincunx Нет, любой язык разрешен.
user80551

Ответы:

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 и сдвигаем их в конце для тривиального повышения производительности. Я включил его здесь, потому что обычно он примерно на 0,5% быстрее, чем fac1.

Гольф версия fac1(потому что я могу), 220В

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]
александр-Brett
источник
1
Если бэкэнд GMP включает функцию сдвига битов, то вы можете сохранить числа еще меньше, разделив каждое число в списке на 2, пока оно не станет четным, а затем сделав один сдвиг в конце.
Питер Тейлор
Откуда вы взяли gmpy2? $ python Python 2.7.3 (по умолчанию, 27 февраля 2014 г., 19:58:35) [GCC 4.6.3] в linux2 Для получения дополнительной информации введите «help», «copyright», «credits» или «license». >>> из gmpy2 import mpz Traceback (последний вызов был последним): файл "<stdin>", строка 1, в <module> ImportError: нет модуля с именем gmpy2 >>>
user80551
@ 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 Питера Люшни (спасибо @ полуэкстрасичный) и лицензированный по лицензии MIT, в нем используется алгоритм «вложенного квадрата факторизации простых чисел», предложенный Albert Schönhage et al. (согласно странице описания факторных алгоритмов Лушного ).

Я немного адаптировал алгоритм, чтобы использовать Java BigInteger и не использовать справочную таблицу для 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);
    }
}
14mRh4X0r
источник
Составлено сgcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r
1

Python 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")

Очевидно, не самый креативный ответ, но на самом деле есть только один способ сделать факториал ....

Дверная ручка
источник
Пожалуйста, дайте оценку, см. Мое редактирование. Выпуклость , вероятно , будет , потому что ваша машина лучше , чем у меня.
user80551
1

Perl + C, n = около 3 миллионов

Здесь я использую библиотеку Math :: BigInt :: GMP, доступную на CPAN, которая обеспечивает значительное увеличение скорости для основных объектов Perl 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 внутренне использует двоичное, а не десятичное представление, и для его распечатки требуется преобразование двоичного в десятичное.

tobyink
источник
1
Я думаю, что GMP считается внешним ресурсом. (Хотя это, безусловно, делает вещи намного проще, чем реализация простой факторизации и умножения Шёнхаге-Штрассена с нуля.)
r3mainer
3
Я предполагал, что «внешний ресурс» относится к поиску решений из предварительно вычисленного набора ответов в базе данных, веб-службе и т. Д.
tobyink
Squeamish: библиотеки обычно не считаются внешними ресурсами, если у них нет функции, которая подпадает под правило скучных лазеек.
Александр-Бретт
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для расчета.

Я работаю в режиме Release, вне 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;)
Ктулху
1
BigInteger для .NET, вероятно, не реализовал алгоритмы для умножения больших чисел, как Карацуба или Тоом-3. Если это так, это хороший пример того, как Python быстрее.
Керни
1

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

Какого черта, вот мой претендент на "Сколько вы можете медленно размножаться?"

bc - это «язык калькулятора произвольной точности», но, к сожалению, довольно медленный:

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

Примерно за 10 секунд на моем MacBook Pro середины 2012 года (2,3 ГГц Intel Core i7) эталонный ответ Python может вычислить 122000 !, но этот скрипт bc может вычислить только 23600 !.

Наоборот 10000! занимает 1,5 с с помощью сценария Python Reference, но сценарий bc занимает 50 с.

О, Боже.

Цифровая травма
источник
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

Питон 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

Сортировка слиянием у.

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()+" @ "+
    }
}

BigIntegerс медленными

Рекомендации для высокоскоростных библиотек Java с произвольной точностью? :П

cjfaure
источник
Могу ли я украсть ваш код, чтобы сделать его многопоточным?
Симон Куанг
@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

Python 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

Ruby 2.1

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

РЕДАКТИРОВАТЬ: улучшено с 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. Тривиальный алгоритм в Ruby 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"
kernigh
источник
0

Юлия - Счет = 15,194

Используя тот же подход, что и в эталонной программе ... то есть

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

Таким образом, он использует уменьшение, базовую операцию двоичного умножения и диапазон (в этом случае, используя big (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

ткл, 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; может n может стать больше на локальном интерпретаторе tcl, которого у меня нет.

sergiol
источник