Python thực sự chậm như thế nào? (Hoặc ngôn ngữ của bạn nhanh như thế nào?)


149

Tôi có mã này mà tôi đã viết bằng Python / NumPy

from __future__ import division
import numpy as np
import itertools

n = 6
iters = 1000
firstzero = 0
bothzero = 0
""" The next line iterates over arrays of length n+1 which contain only -1s and 1s """
for S in itertools.product([-1, 1], repeat=n+1):
    """For i from 0 to iters -1 """
    for i in xrange(iters):
        """ Choose a random array of length n.
            Prob 1/4 of being -1, prob 1/4 of being 1 and prob 1/2 of being 0. """
        F = np.random.choice(np.array([-1, 0, 0, 1], dtype=np.int8), size=n)
        """The next loop just makes sure that F is not all zeros."""
        while np.all(F == 0):
            F = np.random.choice(np.array([-1, 0, 0, 1], dtype=np.int8), size=n)
        """np.convolve(F, S, 'valid') computes two inner products between
        F and the two successive windows of S of length n."""
        FS = np.convolve(F, S, 'valid')
        if FS[0] == 0:
            firstzero += 1
        if np.all(FS == 0):
            bothzero += 1

print("firstzero: %i" % firstzero)
print("bothzero: %i" % bothzero)

Nó đang đếm số lần tích chập của hai mảng ngẫu nhiên, một mảng dài hơn một mảng, với phân phối xác suất cụ thể, có 0 ở vị trí đầu tiên hoặc 0 ở cả hai vị trí.

Tôi đã đặt cược với một người bạn nói rằng Python là một ngôn ngữ khủng khiếp để viết mã trong đó cần phải nhanh. Phải mất 9 giây trên máy tính của tôi. Ông nói rằng nó có thể được thực hiện nhanh hơn 100 lần nếu được viết bằng "ngôn ngữ phù hợp".

Thách thức là để xem liệu mã này thực sự có thể bằng cách thực hiện nhanh hơn 100 lần trong bất kỳ ngôn ngữ nào bạn chọn. Tôi sẽ kiểm tra mã của bạn và nhanh nhất một tuần kể từ bây giờ sẽ giành chiến thắng. Nếu bất cứ ai đạt dưới 0,09 thì họ sẽ tự động thắng và tôi thua.

Trạng thái

  • Con trăn . Tăng tốc 30 lần bởi Alistair Buxon! Mặc dù không phải là giải pháp nhanh nhất nhưng thực tế nó là thứ tôi thích.
  • Octave . Tăng tốc 100 lần bởi @Thethos.
  • Rỉ sét . Tăng tốc 500 lần bởi @dbaupp.
  • C ++ . Tăng tốc gấp 570 lần bởi Guy Sirton.
  • C . Tăng tốc 727 lần bởi @ace.
  • C ++ . Nhanh đến không ngờ bởi @Stefan.

Các giải pháp nhanh nhất hiện nay quá nhanh để có thời gian hợp lý. Do đó, tôi đã tăng n lên 10 và đặt iters = 100000 để so sánh những cái tốt nhất. Theo biện pháp này nhanh nhất là.

  • C . 7.5s bởi @ace.
  • C ++ . 1 giây bởi @Stefan.

Máy của tôi Thời gian sẽ được chạy trên máy của tôi. Đây là bản cài đặt Ubuntu tiêu chuẩn trên Bộ xử lý tám lõi AMD FX-8350. Điều này cũng có nghĩa là tôi cần để có thể chạy mã của bạn.

Theo dõi được đăng lên Vì cuộc thi này quá dễ dàng để có được tốc độ x100, tôi đã đăng một bài theo dõi cho những người muốn thực hiện chuyên môn guru tốc độ của họ. Xem Python thực sự chậm như thế nào (Phần II)?

Câu trả lời:


61

Ma thuật bit C ++

0,84ms với RNG đơn giản, 1,67ms với c ++ 11 std :: knuth

0,16ms với sửa đổi thuật toán nhỏ (xem chỉnh sửa bên dưới)

Việc thực hiện python chạy trong 7,97 giây trên giàn khoan của tôi. Vì vậy, đây là 9488 đến 4772 lần nhanh hơn tùy thuộc vào RNG bạn chọn.

#include <iostream>
#include <bitset>
#include <random>
#include <chrono>
#include <stdint.h>
#include <cassert>
#include <tuple>

#if 0
// C++11 random
std::random_device rd;
std::knuth_b gen(rd());

uint32_t genRandom()
{
    return gen();
}
#else
// bad, fast, random.

uint32_t genRandom()
{
    static uint32_t seed = std::random_device()();
    auto oldSeed = seed;
    seed = seed*1664525UL + 1013904223UL; // numerical recipes, 32 bit
    return oldSeed;
}
#endif

#ifdef _MSC_VER
uint32_t popcnt( uint32_t x ){ return _mm_popcnt_u32(x); }
#else
uint32_t popcnt( uint32_t x ){ return __builtin_popcount(x); }
#endif



std::pair<unsigned, unsigned> convolve()
{
    const uint32_t n = 6;
    const uint32_t iters = 1000;
    unsigned firstZero = 0;
    unsigned bothZero = 0;

    uint32_t S = (1 << (n+1));
    // generate all possible N+1 bit strings
    // 1 = +1
    // 0 = -1
    while ( S-- )
    {
        uint32_t s1 = S % ( 1 << n );
        uint32_t s2 = (S >> 1) % ( 1 << n );
        uint32_t fmask = (1 << n) -1; fmask |= fmask << 16;
        static_assert( n < 16, "packing of F fails when n > 16.");


        for( unsigned i = 0; i < iters; i++ )
        {
            // generate random bit mess
            uint32_t F;
            do {
                F = genRandom() & fmask;
            } while ( 0 == ((F % (1 << n)) ^ (F >> 16 )) );

            // Assume F is an array with interleaved elements such that F[0] || F[16] is one element
            // here MSB(F) & ~LSB(F) returns 1 for all elements that are positive
            // and  ~MSB(F) & LSB(F) returns 1 for all elements that are negative
            // this results in the distribution ( -1, 0, 0, 1 )
            // to ease calculations we generate r = LSB(F) and l = MSB(F)

            uint32_t r = F % ( 1 << n );
            // modulo is required because the behaviour of the leftmost bit is implementation defined
            uint32_t l = ( F >> 16 ) % ( 1 << n );

            uint32_t posBits = l & ~r;
            uint32_t negBits = ~l & r;
            assert( (posBits & negBits) == 0 );

            // calculate which bits in the expression S * F evaluate to +1
            unsigned firstPosBits = ((s1 & posBits) | (~s1 & negBits));
            // idem for -1
            unsigned firstNegBits = ((~s1 & posBits) | (s1 & negBits));

            if ( popcnt( firstPosBits ) == popcnt( firstNegBits ) )
            {
                firstZero++;

                unsigned secondPosBits = ((s2 & posBits) | (~s2 & negBits));
                unsigned secondNegBits = ((~s2 & posBits) | (s2 & negBits));

                if ( popcnt( secondPosBits ) == popcnt( secondNegBits ) )
                {
                    bothZero++;
                }
            }
        }
    }

    return std::make_pair(firstZero, bothZero);
}

int main()
{
    typedef std::chrono::high_resolution_clock clock;
    int rounds = 1000;
    std::vector< std::pair<unsigned, unsigned> > out(rounds);

    // do 100 rounds to get the cpu up to speed..
    for( int i = 0; i < 10000; i++ )
    {
        convolve();
    }


    auto start = clock::now();

    for( int i = 0; i < rounds; i++ )
    {
        out[i] = convolve();
    }

    auto end = clock::now();
    double seconds = std::chrono::duration_cast< std::chrono::microseconds >( end - start ).count() / 1000000.0;

#if 0
    for( auto pair : out )
        std::cout << pair.first << ", " << pair.second << std::endl;
#endif

    std::cout << seconds/rounds*1000 << " msec/round" << std::endl;

    return 0;
}

Biên dịch trong 64-bit cho các thanh ghi bổ sung. Khi sử dụng trình tạo ngẫu nhiên đơn giản, các vòng lặp trong convolve () chạy mà không có bất kỳ quyền truy cập bộ nhớ nào, tất cả các biến được lưu trữ trong các thanh ghi.

Cách thức hoạt động: thay vì lưu trữ SFnhư các mảng trong bộ nhớ, nó được lưu trữ dưới dạng các bit trong một uint32_t.
Đối với S, các nbit có ý nghĩa nhỏ nhất được sử dụng trong đó một bit set biểu thị +1 và bit unset biểu thị -1.
Fyêu cầu ít nhất 2 bit để tạo phân phối [-1, 0, 0, 1]. Điều này được thực hiện bằng cách tạo ra các bit ngẫu nhiên và kiểm tra 16 bit có ý nghĩa nhất (được gọi r) và 16 bit có ý nghĩa nhất (được gọi l). Nếu l & ~rchúng ta giả sử rằng F là +1, nếu ~l & rchúng ta giả sử đó Flà -1. Mặt khác Flà 0. Điều này tạo ra phân phối mà chúng tôi đang tìm kiếm.

Bây giờ chúng ta có S, posBitsvới một bit được đặt trên mọi vị trí trong đó F == 1 và negBitsvới một bit được đặt trên mọi vị trí trong đó F == -1.

Chúng tôi có thể chứng minh rằng F * S(trong đó * biểu thị phép nhân) ước tính thành +1 theo điều kiện (S & posBits) | (~S & negBits). Chúng tôi cũng có thể tạo logic tương tự cho tất cả các trường hợp F * Sước tính đến -1. Và cuối cùng, chúng ta biết rằng sum(F * S)ước tính là 0 khi và chỉ khi có một lượng bằng nhau -1 và 1 trong kết quả. Điều này rất dễ tính toán bằng cách đơn giản so sánh số lượng bit +1 và -1 bit.

Việc triển khai này sử dụng ints 32 bit và mức tối đa nđược chấp nhận là 16. Có thể chia tỷ lệ triển khai thành 31 bit bằng cách sửa đổi mã tạo ngẫu nhiên và 63 bit bằng cách sử dụng uint64_t thay vì uint32_t.

biên tập

Hàm kết hợp folowing:

std::pair<unsigned, unsigned> convolve()
{
    const uint32_t n = 6;
    const uint32_t iters = 1000;
    unsigned firstZero = 0;
    unsigned bothZero = 0;
    uint32_t fmask = (1 << n) -1; fmask |= fmask << 16;
    static_assert( n < 16, "packing of F fails when n > 16.");


    for( unsigned i = 0; i < iters; i++ )
    {
        // generate random bit mess
        uint32_t F;
        do {
            F = genRandom() & fmask;
        } while ( 0 == ((F % (1 << n)) ^ (F >> 16 )) );

        // Assume F is an array with interleaved elements such that F[0] || F[16] is one element
        // here MSB(F) & ~LSB(F) returns 1 for all elements that are positive
        // and  ~MSB(F) & LSB(F) returns 1 for all elements that are negative
        // this results in the distribution ( -1, 0, 0, 1 )
        // to ease calculations we generate r = LSB(F) and l = MSB(F)

        uint32_t r = F % ( 1 << n );
        // modulo is required because the behaviour of the leftmost bit is implementation defined
        uint32_t l = ( F >> 16 ) % ( 1 << n );

        uint32_t posBits = l & ~r;
        uint32_t negBits = ~l & r;
        assert( (posBits & negBits) == 0 );

        uint32_t mask = posBits | negBits;
        uint32_t totalBits = popcnt( mask );
        // if the amount of -1 and +1's is uneven, sum(S*F) cannot possibly evaluate to 0
        if ( totalBits & 1 )
            continue;

        uint32_t adjF = posBits & ~negBits;
        uint32_t desiredBits = totalBits / 2;

        uint32_t S = (1 << (n+1));
        // generate all possible N+1 bit strings
        // 1 = +1
        // 0 = -1
        while ( S-- )
        {
            // calculate which bits in the expression S * F evaluate to +1
            auto firstBits = (S & mask) ^ adjF;
            auto secondBits = (S & ( mask << 1 ) ) ^ ( adjF << 1 );

            bool a = desiredBits == popcnt( firstBits );
            bool b = desiredBits == popcnt( secondBits );
            firstZero += a;
            bothZero += a & b;
        }
    }

    return std::make_pair(firstZero, bothZero);
}

cắt thời gian chạy xuống 0.160-0.161ms. Unroll loop unroll (không được mô tả ở trên) làm cho 0.150. Trường hợp n = 10, iter = 100000 không quan trọng chạy dưới 250ms. Tôi chắc chắn rằng tôi có thể có được nó dưới 50ms bằng cách tận dụng các lõi bổ sung nhưng điều đó quá dễ dàng.

Điều này được thực hiện bằng cách làm cho nhánh vòng lặp bên trong tự do và hoán đổi vòng lặp F và S.
Nếu bothZerokhông bắt buộc, tôi có thể giảm thời gian chạy xuống 0,02ms bằng cách lặp đi lặp lại trên tất cả các mảng S có thể.


3
Bạn có thể cung cấp một phiên bản thân thiện với gcc và dòng lệnh của bạn sẽ được vui lòng không? Tôi không chắc chắn tôi có thể kiểm tra nó hiện tại.

Tôi không biết gì về điều này nhưng google nói với tôi rằng __builtin_popcount có thể là sự thay thế cho _mm_popcnt_u32 ().

3
Mã được cập nhật, sử dụng chuyển đổi #ifdef để chọn lệnh popcnt chính xác. Nó biên dịch với -std=c++0x -mpopcnt -O2và mất 1,01ms để chạy ở chế độ 32 bit (tôi không có phiên bản GCC 64 bit trong tay).
Stefan

Bạn có thể làm cho nó in đầu ra? Tôi không chắc liệu nó có thực sự làm bất cứ điều gì hiện tại không :)

7
Bạn rõ ràng là một phù thủy. + 1
BurntPizza

76

Python2.7 + Numpy 1.8.1: 10.242 s

Fortran 90+: 0,029 s 0,003 s 0,022 s 0,010 s

Chết tiệt, bạn thua cược! Không phải là một sự song song ở đây nữa, chỉ cần Fortran 90+ thẳng.

EDIT Tôi đã sử dụng thuật toán của Guy Sirton để hoán vị mảng S(tìm tốt: D). Tôi dường như cũng có các -g -tracebackcờ biên dịch hoạt động đang làm chậm mã này xuống còn khoảng 0,017 giây. Hiện tại, tôi đang biên dịch cái này là

ifort -fast -o convolve convolve_random_arrays.f90

Đối với những người không có ifort, bạn có thể sử dụng

gfortran -O3 -ffast-math -o convolve convolve_random_arrays.f90

EDIT 2 : Việc giảm thời gian chạy là do tôi đã làm sai điều gì đó trước đây và nhận được câu trả lời không chính xác. Làm điều đó đúng cách rõ ràng là chậm hơn. Tôi vẫn không thể tin rằng C ++ nhanh hơn của tôi, vì vậy có lẽ tôi sẽ dành một chút thời gian trong tuần này để cố gắng điều chỉnh những thứ nhảm nhí này để tăng tốc nó.

EDIT 3 : Chỉ cần thay đổi phần RNG bằng cách sử dụng phần RNG của BSD (như được đề xuất bởi Sampo Smolander) và loại bỏ sự phân chia liên tục theo m1, tôi đã cắt thời gian chạy giống như câu trả lời C ++ của Guy Sirton . Sử dụng mảng tĩnh (theo đề xuất của Sharpie) giảm thời gian chạy xuống dưới thời gian chạy C ++! Yay Fortran! :CƯỜI MỞ MIỆNG

EDIT 4 Rõ ràng điều này không biên dịch (với gfortran) và chạy chính xác (giá trị không chính xác) vì các số nguyên đang vượt quá giới hạn của chúng. Tôi đã thực hiện các chỉnh sửa để đảm bảo nó hoạt động, nhưng điều này đòi hỏi phải có ifort 11+ hoặc gfortran 4.7+ (hoặc một trình biên dịch khác cho phép iso_fortran_envint64loại F2008 ).

Đây là mã:

program convolve_random_arrays
   use iso_fortran_env
   implicit none
   integer(int64), parameter :: a1 = 1103515245
   integer(int64), parameter :: c1 = 12345
   integer(int64), parameter :: m1 = 2147483648
   real, parameter ::    mi = 4.656612873e-10 ! 1/m1
   integer, parameter :: n = 6
   integer :: p, pmax, iters, i, nil(0:1), seed
   !integer, allocatable ::  F(:), S(:), FS(:)
   integer :: F(n), S(n+1), FS(2)

   !n = 6
   !allocate(F(n), S(n+1), FS(2))
   iters = 1000
   nil = 0

   !call init_random_seed()

   S = -1
   pmax = 2**(n+1)
   do p=1,pmax
      do i=1,iters
         F = rand_int_array(n)
         if(all(F==0)) then
            do while(all(F==0))
               F = rand_int_array(n)
            enddo
         endif

         FS = convolve(F,S)

         if(FS(1) == 0) then
            nil(0) = nil(0) + 1
            if(FS(2) == 0) nil(1) = nil(1) + 1
         endif

      enddo
      call permute(S)
   enddo

   print *,"first zero:",nil(0)
   print *," both zero:",nil(1)

 contains
   pure function convolve(x, h) result(y)
!x is the signal array
!h is the noise/impulse array
      integer, dimension(:), intent(in) :: x, h
      integer, dimension(abs(size(x)-size(h))+1) :: y
      integer:: i, j, r
      y(1) = dot_product(x,h(1:n-1))
      y(2) = dot_product(x,h(2:n  ))
   end function convolve

   pure subroutine permute(x)
      integer, intent(inout) :: x(:)
      integer :: i

      do i=1,size(x)
         if(x(i)==-1) then
            x(i) = 1
            return
         endif
         x(i) = -1
      enddo
   end subroutine permute

   function rand_int_array(i) result(x)
     integer, intent(in) :: i
     integer :: x(i), j
     real :: y
     do j=1,i
        y = bsd_rng()
        if(y <= 0.25) then
           x(j) = -1
        else if (y >= 0.75) then
           x(j) = +1
        else
           x(j) = 0
        endif
     enddo
   end function rand_int_array

   function bsd_rng() result(x)
      real :: x
      integer(int64) :: b=3141592653
      b = mod(a1*b + c1, m1)
      x = real(b)*mi
   end function bsd_rng
end program convolve_random_arrays

Tôi cho rằng câu hỏi bây giờ là bạn sẽ ngừng sử dụng Python chậm như mật mía và sử dụng Fortran nhanh như điện tử có thể di chuyển;).


1
Không phải câu lệnh tình huống sẽ nhanh hơn hàm tạo? Trừ khi bạn mong đợi một số loại tăng tốc dự đoán nhánh / bộ đệm / dòng / vv?
OrangeDog

17
Tốc độ nên được so sánh trên cùng một máy. Thời gian chạy nào bạn nhận được cho mã của OP?
nbubis

3
Câu trả lời C ++ thực hiện trình tạo số ngẫu nhiên rất nhẹ của riêng nó. Câu trả lời của bạn đã sử dụng mặc định đi kèm với trình biên dịch, có thể chậm hơn?
Sampo Smolander

3
Ngoài ra, ví dụ C ++ dường như đang sử dụng các mảng được phân bổ tĩnh. Hãy thử sử dụng các mảng có độ dài cố định được đặt ở thời gian biên dịch và xem liệu nó có tắt bất kỳ lúc nào không.
Sharpie

1
@KyleKanos @Lembik vấn đề là việc gán số nguyên trong fortran không sử dụng ngầm định đặc tả int64, do đó các số là int32 trước khi thực hiện bất kỳ chuyển đổi nào. Mã phải là: integer(int64) :: b = 3141592653_int64cho tất cả int64. Đây là một phần của tiêu chuẩn fortran và được lập trình viên mong đợi bằng ngôn ngữ lập trình khai báo kiểu. (lưu ý rằng tất nhiên các cài đặt mặc định có thể ghi đè lên điều này)
zeroth

69

Python 2.7 - 0.882s 0.283s

(Bản gốc của OP: 6.404s)

Chỉnh sửa: Tối ưu hóa của Steven Rumbalski bằng cách tính toán trước các giá trị F. Với tối ưu hóa này, cpython đánh bại 0,365s của pypy.

import itertools
import operator
import random

n=6
iters = 1000
firstzero = 0
bothzero = 0

choicesF = filter(any, itertools.product([-1, 0, 0, 1], repeat=n))

for S in itertools.product([-1,1], repeat = n+1):
    for i in xrange(iters):
        F = random.choice(choicesF)
        if not sum(map(operator.mul, F, S[:-1])):
            firstzero += 1
            if not sum(map(operator.mul, F, S[1:])):
                bothzero += 1

print "firstzero", firstzero
print "bothzero", bothzero

Mã ban đầu của OP sử dụng các mảng nhỏ như vậy không có lợi ích gì khi sử dụng Numpy, vì việc triển khai python thuần này chứng minh. Nhưng cũng thấy cách triển khai khó hiểu này nhanh hơn ba lần so với mã của tôi.

Tôi cũng tối ưu hóa bằng cách bỏ qua phần còn lại của tích chập nếu kết quả đầu tiên không bằng không.


11
Với pypy này chạy trong khoảng 0,5 giây.
Alistair Buxton

2
Bạn nhận được một tốc độ tăng tốc thuyết phục hơn nhiều nếu bạn đặt n = 10. Tôi nhận được 19 giây so với 4.6 cho cpython so với pypy.

3
Một tối ưu hóa khác sẽ là tính toán trước khả năng Fvì chỉ có 4032 người trong số họ. Xác định choicesF = filter(any, itertools.product([-1, 0, 0, 1], repeat=n))bên ngoài các vòng lặp. Sau đó, trong nội bộ xác định F = random.choice(choicesF). Tôi nhận được một tăng tốc 3x với cách tiếp cận như vậy.
Steven Rumbalski

3
Làm thế nào về việc biên dịch này trong Cython? Sau đó thêm một vài loại tĩnh khéo léo?
Thane Brimhall

2
Đặt mọi thứ trong một hàm và gọi nó ở cuối. Điều đó bản địa hóa các tên, điều này cũng làm cho việc tối ưu hóa được đề xuất bởi @riffraff hoạt động. Ngoài ra, di chuyển việc tạo range(iters)ra các vòng lặp. Nhìn chung, tôi nhận được một sự tăng tốc khoảng 7% so với câu trả lời rất hay của bạn.
WolframH

44

Rỉ sét: 0,011s

Python nguyên bản: 8.3

Một bản dịch thẳng của Python gốc.

extern crate rand;

use rand::Rng;

static N: uint = 6;
static ITERS: uint = 1000;

fn convolve<T: Num>(into: &mut [T], a: &[T], b: &[T]) {
    // we want `a` to be the longest array
    if a.len() < b.len() {
        convolve(into, b, a);
        return
    }

    assert_eq!(into.len(), a.len() - b.len() + 1);

    for (n,place) in into.mut_iter().enumerate() {
        for (x, y) in a.slice_from(n).iter().zip(b.iter()) {
            *place = *place + *x * *y
        }
    }
}

fn main() {
    let mut first_zero = 0;
    let mut both_zero = 0;
    let mut rng = rand::XorShiftRng::new().unwrap();

    for s in PlusMinus::new() {
        for _ in range(0, ITERS) {
            let mut f = [0, .. N];
            while f.iter().all(|x| *x == 0) {
                for p in f.mut_iter() {
                    match rng.gen::<u32>() % 4 {
                        0 => *p = -1,
                        1 | 2 => *p = 0,
                        _ => *p = 1
                    }
                }
            }

            let mut fs = [0, .. 2];
            convolve(fs, s, f);

            if fs[0] == 0 { first_zero += 1 }
            if fs.iter().all(|&x| x == 0) { both_zero += 1 }
        }
    }

    println!("{}\n{}", first_zero, both_zero);
}



/// An iterator over [+-]1 arrays of the appropriate length
struct PlusMinus {
    done: bool,
    current: [i32, .. N + 1]
}
impl PlusMinus {
    fn new() -> PlusMinus {
        PlusMinus { done: false, current: [-1, .. N + 1] }
    }
}

impl Iterator<[i32, .. N + 1]> for PlusMinus {
    fn next(&mut self) -> Option<[i32, .. N+1]> {
        if self.done {
            return None
        }

        let ret = self.current;

        // a binary "adder", that just adds one to a bit vector (where
        // -1 is the zero, and 1 is the one).
        for (i, place) in self.current.mut_iter().enumerate() {
            *place = -*place;
            if *place == 1 {
                break
            } else if i == N {
                // we've wrapped, so we want to stop after this one
                self.done = true
            }
        }

        Some(ret)
    }
}
  • Tổng hợp với --opt-level=3
  • Trình biên dịch rỉ sét của tôi là một đêm gần đây : ( rustc 0.11-pre-nightly (eea4909 2014-04-24 23:41:15 -0700)chính xác)

Tôi đã nhận nó để biên dịch bằng cách sử dụng phiên bản rỉ sét hàng đêm. Tuy nhiên tôi nghĩ rằng mã là sai. Đầu ra phải là một cái gì đó gần với Firstzero 27215 cảzerzer 12086. Thay vào đó, nó mang lại cho 27367 6481

@Lembik, rất tiếc, đã trộn lẫn giữa tôi abs; đã sửa (không thay đổi thời gian chạy đáng chú ý).
huon

4
Đó là một minh chứng rất hay về tốc độ của rỉ sét.

39

C ++ (VS 2012) - 0,026 giây 0,015s

Python 2.7.6 / Numpy 1.8.1 - 12 giây

Tăng tốc ~ x800.

Khoảng cách sẽ nhỏ hơn rất nhiều nếu các mảng được tích hợp rất lớn ...

#include <vector>
#include <iostream>
#include <ctime>

using namespace std;

static unsigned int seed = 35;

int my_random()
{
   seed = seed*1664525UL + 1013904223UL; // numerical recipes, 32 bit

   switch((seed>>30) & 3)
   {
   case 0: return 0;
   case 1: return -1;
   case 2: return 1;
   case 3: return 0;
   }
   return 0;
}

bool allzero(const vector<int>& T)
{
   for(auto x : T)
   {
      if(x!=0)
      {
         return false;
      }
   }
   return true;
}

void convolve(vector<int>& out, const vector<int>& v1, const vector<int>& v2)
{
   for(size_t i = 0; i<out.size(); ++i)
   {
      int result = 0;
      for(size_t j = 0; j<v2.size(); ++j)
      {
         result += v1[i+j]*v2[j];
      }
      out[i] = result;
   }
}

void advance(vector<int>& v)
{
   for(auto &x : v)
   {
      if(x==-1)
      {
         x = 1;
         return;
      }
      x = -1;
   }
}

void convolve_random_arrays(void)
{
   const size_t n = 6;
   const int two_to_n_plus_one = 128;
   const int iters = 1000;
   int bothzero = 0;
   int firstzero = 0;

   vector<int> S(n+1);
   vector<int> F(n);
   vector<int> FS(2);

   time_t current_time;
   time(&current_time);
   seed = current_time;

   for(auto &x : S)
   {
      x = -1;
   }
   for(int i=0; i<two_to_n_plus_one; ++i)
   {
      for(int j=0; j<iters; ++j)
      {
         do
         {
            for(auto &x : F)
            {
               x = my_random();
            }
         } while(allzero(F));
         convolve(FS, S, F);
         if(FS[0] == 0)
         {
            firstzero++;
            if(FS[1] == 0)
            {
               bothzero++;
            }
         }
      }
      advance(S);
   }
   cout << firstzero << endl; // This output can slow things down
   cout << bothzero << endl; // comment out for timing the algorithm
}

Một vài lưu ý:

  • Hàm ngẫu nhiên đang được gọi trong vòng lặp, vì vậy tôi đã tìm một trình tạo đồng quy tuyến tính trọng lượng rất nhẹ (nhưng nhìn rộng rãi vào các MSB).
  • Đây thực sự chỉ là điểm khởi đầu cho một giải pháp tối ưu.
  • Không mất nhiều thời gian để viết ...
  • Tôi lặp qua tất cả các giá trị của S S[0]là chữ số "ít quan trọng nhất".

Thêm chức năng chính này cho một ví dụ khép kín:

int main(int argc, char** argv)
{
  for(int i=0; i<1000; ++i) // run 1000 times for stop-watch
  {
      convolve_random_arrays();
  }
}

1
Thật. Kích thước nhỏ của các mảng trong mã của OP có nghĩa là sử dụng numpy thực sự là một thứ tự cường độ chậm hơn so với trăn thẳng.
Alistair Buxton

2
Bây giờ x800 là những gì tôi đang nói về!

Rất đẹp! Tôi đã tăng tốc độ mã của tôi vì advancechức năng của bạn , vì vậy mã của tôi bây giờ nhanh hơn mã của bạn: P (nhưng cạnh tranh rất tốt!)
Kyle Kanos

1
@lembik đúng như Mat nói. Bạn cần supprt C ++ 11 và một chức năng chính. Hãy cho tôi biết nếu bạn cần thêm trợ giúp để chạy nó ...
Guy Sirton

2
Tôi vừa thử nghiệm điều này và có thể cạo thêm 20% bằng cách sử dụng mảng đơn giản thay vì std :: vector ..
PlasmaHH

21

C

Mất 0,015 giây trên máy của tôi, với mã gốc của OP mất ~ 7,7 giây. Đã cố gắng tối ưu hóa bằng cách tạo mảng ngẫu nhiên và kết hợp trong cùng một vòng lặp, nhưng dường như nó không tạo ra nhiều khác biệt.

Mảng đầu tiên được tạo bằng cách lấy một số nguyên, viết nó thành nhị phân và thay đổi tất cả từ 1 thành -1 và tất cả từ 0 đến 1. Phần còn lại sẽ rất đơn giản.

Chỉnh sửa: thay vì có nnhư một int, bây giờ chúng ta có nmột hằng số được xác định vĩ mô, vì vậy chúng ta có thể sử dụng int arr[n];thay vì malloc.

Edit2: Thay vì rand()chức năng tích hợp sẵn, giờ đây nó thực hiện PRNG xorshift. Ngoài ra, rất nhiều câu lệnh có điều kiện được loại bỏ khi tạo mảng ngẫu nhiên.

Hướng dẫn biên dịch:

gcc -O3 -march=native -fwhole-program -fstrict-aliasing -ftree-vectorize -Wall ./test.c -o ./test

Mã số:

#include <stdio.h>
#include <time.h>

#define n (6)
#define iters (1000)
unsigned int x,y=34353,z=57768,w=1564; //PRNG seeds

/* xorshift PRNG
 * Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
 * Used under CC-By-SA */
int myRand() {
    unsigned int t;
    t = x ^ (x << 11);
    x = y; y = z; z = w;
    return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}

int main() {
    int firstzero=0, bothzero=0;
    int arr[n+1];
    unsigned int i, j;
    x=(int)time(NULL);

    for(i=0; i< 1<<(n+1) ; i++) {
        unsigned int tmp=i;
        for(j=0; j<n+1; j++) {
            arr[j]=(tmp&1)*(-2)+1;
            tmp>>=1;
        }
        for(j=0; j<iters; j++) {
            int randArr[n];
            unsigned int k, flag=0;
            int first=0, second=0;
            do {
                for(k=0; k<n; k++) {
                    randArr[k]=(1-(myRand()&3))%2;
                    flag+=(randArr[k]&1);
                    first+=arr[k]*randArr[k];
                    second+=arr[k+1]*randArr[k];
                }
            } while(!flag);
            firstzero+=(!first);
            bothzero+=(!first&&!second);
        }
    }
    printf("firstzero %d\nbothzero %d\n", firstzero, bothzero);
    return 0;
}

1
Tôi đã thử nghiệm điều này. Nó rất nhanh (thử n = 10) và cho đầu ra nhìn chính xác. Cảm ơn bạn.

Việc triển khai này không tuân theo nguyên bản vì nếu vectơ ngẫu nhiên là tất cả các số 0 thì chỉ phần tử cuối cùng sẽ được tạo lại. Trong bản gốc, toàn bộ vector sẽ là. Bạn cần kèm theo vòng lặp do{}while(!flag)đó hoặc một cái gì đó để có hiệu lực. Tôi không hy vọng nó sẽ thay đổi thời gian chạy nhiều (có thể làm cho nó nhanh hơn).
Guy Sirton

@ Guy Sirton ý rằng trước khi continue;tuyên bố tôi giao -1đến k, vì vậy ksẽ lặp từ 0 một lần nữa.
ace_HongKongInependence

1
@ace ah! bạn đúng. Tôi đã quét quá nhanh và có vẻ như đó là -=thay vì =-:-) Một vòng lặp while sẽ dễ đọc hơn.
Guy Sirton

17

J

Tôi không mong đợi đánh bại bất kỳ ngôn ngữ được biên dịch nào và một cái gì đó cho tôi biết rằng nó sẽ cần một cỗ máy kỳ diệu để có được ít hơn 0,09 giây với điều này, nhưng dù sao tôi cũng muốn gửi J này, vì nó khá trơn tru.

NB. constants
num =: 6
iters =: 1000

NB. convolve
NB. take the multiplication table                */
NB. then sum along the NE-SW diagonals           +//.
NB. and keep the longest ones                    #~ [: (= >./) #/.
NB. operate on rows of higher dimensional lists  " 1
conv =: (+//. #~ [: (= >./) #/.) @: (*/) " 1

NB. main program
S  =: > , { (num+1) # < _1 1                NB. all {-1,1}^(num+1)
F  =: (3&= - 0&=) (iters , num) ?@$ 4       NB. iters random arrays of length num
FS =: ,/ S conv/ F                          NB. make a convolution table
FB =: +/ ({. , *./)"1 ] 0 = FS              NB. first and both zero
('first zero ',:'both zero ') ,. ":"0 FB    NB. output results

Điều này mất khoảng 0,5 giây trên máy tính xách tay từ thập kỷ trước, chỉ nhanh hơn khoảng 20 lần so với Python trong câu trả lời. Hầu hết thời gian được dành cho convvì chúng tôi viết nó một cách lười biếng (chúng tôi tính toán toàn bộ tích chập) và tổng quát.

Vì chúng tôi biết những điều về SF, chúng tôi có thể tăng tốc mọi thứ bằng cách tối ưu hóa cụ thể cho chương trình này. Điều tốt nhất mà tôi có thể đưa ra là, hãy conv =: ((num, num+1) { +//.)@:(*/)"1chọn cụ thể hai số tương ứng từ tổng số đường chéo cho đến các phần tử dài nhất của tích chập, khoảng một nửa thời gian.


6
J luôn đáng để phục tùng, anh bạn :)
Vitaly Dyatlov

17

Perl - nhanh hơn 9,3 lần ... cải thiện 830%

Trên netbook cổ của tôi, mã của OP mất 53 giây để chạy; Phiên bản của Alistair Buxton mất khoảng 6,5 giây và phiên bản Perl sau mất khoảng 5,7 giây.

use v5.10;
use strict;
use warnings;

use Algorithm::Combinatorics qw( variations_with_repetition );
use List::Util qw( any sum );
use List::MoreUtils qw( pairwise );

my $n         = 6;
my $iters     = 1000;
my $firstzero = 0;
my $bothzero  = 0;

my $variations = variations_with_repetition([-1, 1], $n+1);
while (my $S = $variations->next)
{
  for my $i (1 .. $iters)
  {
    my @F;
    until (@F and any { $_ } @F)
    {
      @F = map +((-1,0,0,1)[rand 4]), 1..$n;
    }

    # The pairwise function doesn't accept array slices,
    # so need to copy into a temp array @S0
    my @S0 = @$S[0..$n-1];

    unless (sum pairwise { $a * $b } @F, @S0)
    {
      $firstzero++;
      my @S1 = @$S[1..$n];  # copy again :-(
      $bothzero++ unless sum pairwise { $a * $b } @F, @S1;
    }
  }
}

say "firstzero ", $firstzero;
say "bothzero ", $bothzero;

12

Python 2.7 - numpy 1.8.1 với các ràng buộc mkl - 0,086s

(Bản gốc của OP: 6.404s) (Con trăn nguyên chất của Buxton: 0.270s)

import numpy as np
import itertools

n=6
iters = 1000

#Pack all of the Ses into a single array
S = np.array( list(itertools.product([-1,1], repeat=n+1)) )

# Create a whole array of test arrays, oversample a bit to ensure we 
# have at least (iters) of them
F = np.random.rand(int(iters*1.1),n)
F = ( F < 0.25 )*-1 + ( F > 0.75 )*1
goodrows = (np.abs(F).sum(1)!=0)
assert goodrows.sum() > iters, "Got very unlucky"
# get 1000 cases that aren't all zero
F = F[goodrows][:iters]

# Do the convolution explicitly for the two 
# slots, but on all of the Ses and Fes at the 
# same time
firstzeros = (F[:,None,:]*S[None,:,:-1]).sum(-1)==0
secondzeros = (F[:,None,:]*S[None,:,1:]).sum(-1)==0

firstzero_count = firstzeros.sum()
bothzero_count = (firstzeros * secondzeros).sum()
print "firstzero", firstzero_count
print "bothzero", bothzero_count

Như Buxton chỉ ra, mã gốc của OP sử dụng các mảng nhỏ như vậy không có lợi ích gì khi sử dụng Numpy. Việc triển khai này thúc đẩy numpy bằng cách thực hiện tất cả các trường hợp F và S cùng một lúc theo cách định hướng mảng. Điều này kết hợp với các ràng buộc mkl cho python dẫn đến việc thực hiện rất nhanh.

Cũng lưu ý rằng chỉ cần tải các thư viện và bắt đầu trình thông dịch mất 0,076 giây nên việc tính toán thực tế mất ~ 0,01 giây, tương tự như giải pháp C ++.


Các ràng buộc mkl là gì và làm cách nào để có được chúng trên Ubuntu?

Chạy python -c "import numpy; numpy.show_config()"sẽ cho bạn biết nếu phiên bản numpy của bạn được biên dịch dựa trên blas / atlas / mkl, v.v. ATLAS là gói toán học tăng tốc miễn phí mà numpy có thể được liên kết với , Intel MKL bạn thường phải trả tiền (trừ khi bạn là học giả) và có thể được liên kết với numpy / scipy .
alemi

Để dễ dàng, hãy sử dụng phân phối trăn anaconda và sử dụng gói tăng tốc . Hoặc sử dụng phân phối enth think .
alemi

Nếu bạn đang ở trên windows, chỉ cần tải xuống numpy từ đây . Trình cài đặt numpy được biên dịch trước được liên kết với MKL.
Tên giả

9

MATLAB 0,024s

Máy tính 1

  • Mã gốc: ~ 3,3 giây
  • Mã của Alistar Buxton: ~ 0,51 s
  • Mã mới của Alistar Buxton: ~ 0,25 s
  • Mã Matlab: ~ 0,024 s (Matlab đã chạy)

Máy tính 2

  • Mã gốc: ~ 6,66 s
  • Mã của Alistar Buxton: ~ 0,64 giây
  • Mã mới của Alistar Buxton :?
  • Matlab: ~ 0,07 s (Matlab đã chạy)
  • Octave: ~ 0,07 giây

Tôi quyết định thử Matlab quá chậm. Nếu bạn biết cách, bạn có thể thoát khỏi hầu hết các vòng lặp (trong Matlab), điều này làm cho nó khá nhanh. Tuy nhiên, yêu cầu bộ nhớ cao hơn so với các giải pháp lặp nhưng điều này sẽ không thành vấn đề nếu bạn không có mảng rất lớn ...

function call_convolve_random_arrays
tic
convolve_random_arrays
toc
end

function convolve_random_arrays

n = 6;
iters = 1000;
firstzero = 0;
bothzero = 0;

rnd = [-1, 0, 0, 1];

S = -1 *ones(1, n + 1);

IDX1 = 1:n;
IDX2 = IDX1 + 1;

for i = 1:2^(n + 1)
    F = rnd(randi(4, [iters, n]));
    sel = ~any(F,2);
    while any(sel)
        F(sel, :) = rnd(randi(4, [sum(sel), n]));
        sel = ~any(F,2);
    end

    sum1 = F * S(IDX1)';
    sel = sum1 == 0;
    firstzero = firstzero + sum(sel);

    sum2 = F(sel, :) * S(IDX2)';
    sel = sum2 == 0;
    bothzero = bothzero + sum(sel);

    S = permute(S); 
end

fprintf('firstzero %i \nbothzero %i \n', firstzero, bothzero);

end

function x = permute(x)

for i=1:length(x)
    if(x(i)==-1)
        x(i) = 1;
            return
    end
        x(i) = -1;
end

end

Đây là những gì tôi làm:

  • sử dụng hàm Kyle Kanos để hoán vị qua S
  • tính tất cả các số ngẫu nhiên n * iters cùng một lúc
  • ánh xạ 1 đến 4 đến [-1 0 0 1]
  • sử dụng phép nhân ma trận (tổng phần tử (F * S (1: 5)) bằng phép nhân ma trận của F * S (1: 5) '
  • cho cả haizero: chỉ tính toán các thành viên điền vào điều kiện đầu tiên

Tôi cho rằng bạn không có MATLAB, điều này quá tệ vì tôi thực sự muốn xem nó so sánh như thế nào ...

(Chức năng có thể chậm hơn trong lần đầu tiên bạn chạy nó.)


Chà, tôi có quãng tám nếu bạn có thể làm cho nó hoạt động cho điều đó ...?

Tôi có thể thử nó - mặc dù tôi chưa bao giờ làm việc với quãng tám.
toán học

Ok, tôi có thể chạy nó như trong octave nếu tôi đặt mã trong một tệp có tên call_convolve_random_arrays.m và sau đó gọi nó từ octave.
toán học

Có cần thêm một số mã để thực sự làm cho nó để làm bất cứ điều gì? Khi tôi thực hiện "octave call_convolve_random_arrays.m" thì nó không xuất ra bất cứ thứ gì. Xem bpaste.net/show/JPtLOCeI3aP3wc3F3aGf

xin lỗi, hãy thử mở quãng tám và chạy nó sau đó Nó sẽ hiển thị Firstzero, cảzerzer và thời gian thực hiện.
toán học

7

Julia: 0,30 giây

Op's Python: 21,36 giây (bộ đôi Core2)

Tăng tốc 71 lần

function countconv()                                                                                                                                                           
    n = 6                                                                                                                                                                      
    iters = 1000                                                                                                                                                               
    firstzero = 0                                                                                                                                                              
    bothzero = 0                                                                                                                                                               
    cprod= Iterators.product(fill([-1,1], n+1)...)                                                                                                                             
    F=Array(Float64,n);                                                                                                                                                        
    P=[-1. 0. 0. 1.]                                                                                                                                                                                                                                                                                                             

    for S in cprod                                                                                                                                                             
        Sm=[S...]                                                                                                                                                              
        for i = 1:iters                                                                                                                                                        
            F=P[rand(1:4,n)]                                                                                                                                                  
            while all(F==0)                                                                                                                                                   
                F=P[rand(1:4,n)]                                                                                                                                              
            end                                                                                                                                                               
            if  dot(reverse!(F),Sm[1:end-1]) == 0                                                                                                                           
                firstzero += 1                                                                                                                                                 
                if dot(F,Sm[2:end]) == 0                                                                                                                              
                    bothzero += 1                                                                                                                                              
                end                                                                                                                                                            
            end                                                                                                                                                                
        end                                                                                                                                                                    
    end
    return firstzero,bothzero
end

Tôi đã thực hiện một số sửa đổi cho câu trả lời của Arman's Julia: Trước hết, tôi đã gói nó trong một hàm, vì các biến toàn cục làm cho suy luận kiểu của Julia và JIT: Biến toàn cục có thể thay đổi loại bất cứ lúc nào và phải được kiểm tra mọi thao tác . Sau đó, tôi đã thoát khỏi các chức năng ẩn danh và hiểu mảng. Chúng không thực sự cần thiết và vẫn còn khá chậm. Julia nhanh hơn với sự trừu tượng cấp thấp hơn ngay bây giờ.

Có nhiều cách khác để làm cho nó nhanh hơn, nhưng đây là một công việc tốt.


Bạn đang đo thời gian trong REPL hoặc chạy toàn bộ tệp từ dòng lệnh?
Aditya

cả hai từ REPL.
dùng20768

6

Ok tôi đang đăng bài này chỉ vì tôi cảm thấy Java cần được trình bày ở đây. Tôi khủng khiếp với các ngôn ngữ khác và tôi thú nhận là không hiểu chính xác vấn đề, vì vậy tôi sẽ cần một số trợ giúp để sửa mã này. Tôi đã đánh cắp hầu hết các ví dụ mã của ace, và sau đó mượn một số đoạn trích từ những người khác. Tôi hy vọng đó không phải là một ...

Một điều tôi muốn chỉ ra là các ngôn ngữ tối ưu hóa trong thời gian chạy cần phải được chạy nhiều lần / nhiều lần để đạt tốc độ tối đa. Tôi nghĩ việc lấy tốc độ tối ưu hóa hoàn toàn (hoặc ít nhất là tốc độ trung bình) là hợp lý bởi vì hầu hết mọi thứ bạn quan tâm với việc chạy nhanh sẽ được chạy rất nhiều lần.

Mã vẫn cần phải được sửa, nhưng tôi vẫn chạy nó để xem tôi sẽ nhận được bao nhiêu lần.

Dưới đây là kết quả trên CPU Intel (R) Xeon (R) E3-1270 V2 @ 3.50GHz trên Ubuntu chạy 1000 lần:

máy chủ: / tmp # thời gian java8 -cp. Kiểm thử

đầu tiên 40000

cả hai 20000

Thời gian chạy đầu tiên: 41 ms Thời gian chạy cuối cùng: 4 ms

người dùng thực 0m5.014s 0m4.664s sys 0m0.268s

Đây là mã crappy của tôi:

public class Tester 
{
    public static void main( String[] args )
    {
        long firstRunTime = 0;
        long lastRunTime = 0;
        String testResults = null;
        for( int i=0 ; i<1000 ; i++ )
        {
            long timer = System.currentTimeMillis();
            testResults = new Tester().runtest();
            lastRunTime = System.currentTimeMillis() - timer;
            if( i ==0 )
            {
                firstRunTime = lastRunTime;
            }
        }
        System.err.println( testResults );
        System.err.println( "first run time: " + firstRunTime + " ms" );
        System.err.println( "last run time: " + lastRunTime + " ms" );
    }

    private int x,y=34353,z=57768,w=1564; 

    public String runtest()
    {
        int n = 6;
        int iters = 1000;
        //#define iters (1000)
        //PRNG seeds

        /* xorshift PRNG
         * Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
         * Used under CC-By-SA */

            int firstzero=0, bothzero=0;
            int[] arr = new int[n+1];
            int i=0, j=0;
            x=(int)(System.currentTimeMillis()/1000l);

            for(i=0; i< 1<<(n+1) ; i++) {
                int tmp=i;
                for(j=0; j<n+1; j++) {
                    arr[j]=(tmp&1)*(-2)+1;
                    tmp>>=1;
                }
                for(j=0; j<iters; j++) {
                    int[] randArr = new int[n];
                    int k=0;
                    long flag = 0;
                    int first=0, second=0;
                    do {
                        for(k=0; k<n; k++) {
                            randArr[k]=(1-(myRand()&3))%2;
                            flag+=(randArr[k]&1);
                            first+=arr[k]*randArr[k];
                            second+=arr[k+1]*randArr[k];
                        }
                    } while(allzero(randArr));
                    if( first == 0 )
                    {
                        firstzero+=1;
                        if( second == 0 )
                        {
                            bothzero++;
                        }
                    }
                }
            }
         return ( "firstzero " + firstzero + "\nbothzero " + bothzero + "\n" );
    }

    private boolean allzero(int[] arr)
    {
       for(int x : arr)
       {
          if(x!=0)
          {
             return false;
          }
       }
       return true;
    }

    public int myRand() 
    {
        long t;
        t = x ^ (x << 11);
        x = y; y = z; z = w;
        return (int)( w ^ (w >> 19) ^ t ^ (t >> 8));
    }
}

Và tôi đã thử chạy mã python sau khi nâng cấp python và cài đặt python-numpy nhưng tôi nhận được điều này:

server:/tmp# python tester.py
Traceback (most recent call last):
  File "peepee.py", line 15, in <module>
    F = np.random.choice(np.array([-1,0,0,1], dtype=np.int8), size = n)
AttributeError: 'module' object has no attribute 'choice'

Nhận xét: Không bao giờ sử dụng currentTimeMillisđể đo điểm chuẩn (sử dụng phiên bản nano trong Hệ thống) và các lần chạy 1k có thể không đủ để tham gia JIT (1,5k cho máy khách và 10k cho máy chủ sẽ là mặc định, mặc dù bạn thường gọi myRand là đủ JITed sẽ khiến một số chức năng của Callstack được biên dịch có thể hoạt động ở đây). Nhưng ít nhất là PNRG yếu là gian lận, nhưng giải pháp C ++ và các chức năng khác cũng vậy, vì vậy tôi đoán điều đó không quá bất công.
Voo

Trên các cửa sổ, bạn cần tránh currentTimeMillis, nhưng đối với linux đối với tất cả các phép đo độ chi tiết rất nhỏ, bạn không cần thời gian nano và cuộc gọi để có được thời gian nano đắt hơn nhiều so với millis. Vì vậy, tôi rất không đồng ý rằng bạn KHÔNG BAO GIỜ nên sử dụng nó.
Chris Seline

Vì vậy, bạn đang viết mã Java cho một triển khai JVM và OS cụ thể? Trên thực tế tôi không chắc chắn hệ điều hành bạn đang sử dụng, bởi vì tôi chỉ kiểm tra trong cây dev HotSpot của tôi và Linux sử dụng gettimeofday(&time, NULL)cho mili giây mà không phải là monotonical và không đưa ra bất cứ đảm bảo độ chính xác (vì vậy trên một số nền tảng / kernel giống hệt nhau các vấn đề như việc triển khai Windows hiện tại của Windows - vì vậy cũng tốt hoặc không có gì cả). Mặt khác, nanoTime sử dụng clock_gettime(CLOCK_MONOTONIC, &tp)một cách rõ ràng cũng là điều phù hợp để sử dụng khi đo điểm chuẩn trên Linux.
Voo

Nó chưa bao giờ gây ra sự cố cho tôi vì tôi đã mã hóa java trên bất kỳ bản phân phối hoặc kernel Linux nào.
Chris Seline

6

Golang phiên bản 45X của python trên máy của tôi ở bên dưới mã Golang:

package main

import (
"fmt"
"time"
)

const (
n     = 6
iters = 1000
)

var (
x, y, z, w = 34353, 34353, 57768, 1564 //PRNG seeds
)

/* xorshift PRNG
 * Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
 * Used under CC-By-SA */
func myRand() int {
var t uint
t = uint(x ^ (x << 11))
x, y, z = y, z, w
w = int(uint(w^w>>19) ^ t ^ (t >> 8))
return w
}

func main() {
var firstzero, bothzero int
var arr [n + 1]int
var i, j int
x = int(time.Now().Unix())

for i = 0; i < 1<<(n+1); i = i + 1 {
    tmp := i
    for j = 0; j < n+1; j = j + 1 {
        arr[j] = (tmp&1)*(-2) + 1
        tmp >>= 1
    }
    for j = 0; j < iters; j = j + 1 {
        var randArr [n]int
        var flag uint
        var k, first, second int
        for {
            for k = 0; k < n; k = k + 1 {
                randArr[k] = (1 - (myRand() & 3)) % 2
                flag += uint(randArr[k] & 1)
                first += arr[k] * randArr[k]
                second += arr[k+1] * randArr[k]
            }
            if flag != 0 {
                break
            }
        }
        if first == 0 {
            firstzero += 1
            if second == 0 {
                bothzero += 1
            }
        }
    }
}
println("firstzero", firstzero, "bothzero", bothzero)
}

và các mã python dưới đây được sao chép từ phía trên:

import itertools
import operator
import random

n=6
iters = 1000
firstzero = 0
bothzero = 0

choicesF = filter(any, itertools.product([-1, 0, 0, 1], repeat=n))

for S in itertools.product([-1,1], repeat = n+1):
    for i in xrange(iters):
        F = random.choice(choicesF)
        if not sum(map(operator.mul, F, S[:-1])):
            firstzero += 1
            if not sum(map(operator.mul, F, S[1:])):
                bothzero += 1

print "firstzero", firstzero
print "bothzero", bothzero

và thời gian dưới đây:

$time python test.py
firstzero 27349
bothzero 12125

real    0m0.477s
user    0m0.461s
sys 0m0.014s

$time ./hf
firstzero 27253 bothzero 12142

real    0m0.011s
user    0m0.008s
sys 0m0.002s

1
bạn đã nghĩ về việc sử dụng "github.com/yanatan16/itertools"? bạn cũng có thể nói rằng nó sẽ hoạt động tốt trong nhiều con khỉ đột?
ymg

5

C # 0.135s

C # dựa trên con trăn đồng bằng của Alistair Buxton : 0,278s
Parallelised C #: 0.135s
Python từ câu hỏi: 5.907s Con
trăn đồng bằng của Alistair: 0.853s

Tôi thực sự không chắc chắn việc thực hiện này là chính xác - đầu ra của nó là khác nhau, nếu bạn nhìn vào kết quả ở phía dưới.

Chắc chắn có nhiều thuật toán tối ưu hơn. Tôi chỉ quyết định sử dụng một thuật toán rất giống với thuật toán Python.

C đơn luồng

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConvolvingArrays
{
    static class Program
    {
        static void Main(string[] args)
        {
            int n=6;
            int iters = 1000;
            int firstzero = 0;
            int bothzero = 0;

            int[] arraySeed = new int[] {-1, 1};
            int[] randomSource = new int[] {-1, 0, 0, 1};
            Random rand = new Random();

            foreach (var S in Enumerable.Repeat(arraySeed, n+1).CartesianProduct())
            {
                for (int i = 0; i < iters; i++)
                {
                    var F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    while (!F.Any(f => f != 0))
                    {
                        F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    }
                    if (Enumerable.Zip(F, S.Take(n), (f, s) => f * s).Sum() == 0)
                    {
                        firstzero++;
                        if (Enumerable.Zip(F, S.Skip(1), (f, s) => f * s).Sum() == 0)
                        {
                            bothzero++;
                        }
                    }
                }
            }

            Console.WriteLine("firstzero {0}", firstzero);
            Console.WriteLine("bothzero {0}", bothzero);
        }

        // itertools.product?
        // http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
        static IEnumerable<IEnumerable<T>> CartesianProduct<T>
            (this IEnumerable<IEnumerable<T>> sequences)
        {
            IEnumerable<IEnumerable<T>> emptyProduct =
              new[] { Enumerable.Empty<T>() };
            return sequences.Aggregate(
              emptyProduct,
              (accumulator, sequence) =>
                from accseq in accumulator
                from item in sequence
                select accseq.Concat(new[] { item }));
        }
    }
}

Song song C #:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConvolvingArrays
{
    static class Program
    {
        static void Main(string[] args)
        {
            int n=6;
            int iters = 1000;
            int firstzero = 0;
            int bothzero = 0;

            int[] arraySeed = new int[] {-1, 1};
            int[] randomSource = new int[] {-1, 0, 0, 1};

            ConcurrentBag<int[]> results = new ConcurrentBag<int[]>();

            // The next line iterates over arrays of length n+1 which contain only -1s and 1s
            Parallel.ForEach(Enumerable.Repeat(arraySeed, n + 1).CartesianProduct(), (S) =>
            {
                int fz = 0;
                int bz = 0;
                ThreadSafeRandom rand = new ThreadSafeRandom();
                for (int i = 0; i < iters; i++)
                {
                    var F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    while (!F.Any(f => f != 0))
                    {
                        F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    }
                    if (Enumerable.Zip(F, S.Take(n), (f, s) => f * s).Sum() == 0)
                    {
                        fz++;
                        if (Enumerable.Zip(F, S.Skip(1), (f, s) => f * s).Sum() == 0)
                        {
                            bz++;
                        }
                    }
                }

                results.Add(new int[] { fz, bz });
            });

            foreach (int[] res in results)
            {
                firstzero += res[0];
                bothzero += res[1];
            }

            Console.WriteLine("firstzero {0}", firstzero);
            Console.WriteLine("bothzero {0}", bothzero);
        }

        // itertools.product?
        // http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
        static IEnumerable<IEnumerable<T>> CartesianProduct<T>
            (this IEnumerable<IEnumerable<T>> sequences)
        {
            IEnumerable<IEnumerable<T>> emptyProduct =
              new[] { Enumerable.Empty<T>() };
            return sequences.Aggregate(
              emptyProduct,
              (accumulator, sequence) =>
                from accseq in accumulator
                from item in sequence
                select accseq.Concat(new[] { item }));
        }
    }

    // http://stackoverflow.com/a/11109361/1030702
    public class ThreadSafeRandom
    {
        private static readonly Random _global = new Random();
        [ThreadStatic]
        private static Random _local;

        public ThreadSafeRandom()
        {
            if (_local == null)
            {
                int seed;
                lock (_global)
                {
                    seed = _global.Next();
                }
                _local = new Random(seed);
            }
        }
        public int Next()
        {
            return _local.Next();
        }
        public int Next(int maxValue)
        {
            return _local.Next(maxValue);
        }
    }
}

Đầu ra thử nghiệm:

Windows (.NET)

C # nhanh hơn nhiều trên Windows. Có lẽ vì .NET nhanh hơn mono.

Thời gian của người dùng và hệ thống dường như không hoạt động (được sử dụng git bashđể định thời gian).

$ time /c/Python27/python.exe numpypython.py
firstzero 27413
bothzero 12073

real    0m5.907s
user    0m0.000s
sys     0m0.000s
$ time /c/Python27/python.exe plainpython.py
firstzero 26983
bothzero 12033

real    0m0.853s
user    0m0.000s
sys     0m0.000s
$ time ConvolvingArrays.exe
firstzero 28526
bothzero 6453

real    0m0.278s
user    0m0.000s
sys     0m0.000s
$ time ConvolvingArraysParallel.exe
firstzero 28857
bothzero 6485

real    0m0.135s
user    0m0.000s
sys     0m0.000s

Linux (đơn âm)

bob@phoebe:~/convolvingarrays$ time python program.py
firstzero 27059
bothzero 12131

real    0m11.932s
user    0m11.912s
sys     0m0.012s
bob@phoebe:~/convolvingarrays$ mcs -optimize+ -debug- program.cs
bob@phoebe:~/convolvingarrays$ time mono program.exe
firstzero 28982
bothzero 6512

real    0m1.360s
user    0m1.532s
sys     0m0.872s
bob@phoebe:~/convolvingarrays$ mcs -optimize+ -debug- parallelprogram.cs
bob@phoebe:~/convolvingarrays$ time mono parallelprogram.exe
firstzero 28857
bothzero 6496

real    0m0.851s
user    0m2.708s
sys     0m3.028s

1
Tôi không nghĩ rằng mã là chính xác như bạn nói. Các đầu ra không đúng.

@Lembik Yea. Tuy nhiên, tôi đánh giá cao nếu ai đó có thể cho tôi biết nó sai ở đâu - mặc dù vậy - tôi không thể hiểu được (chỉ có một sự hiểu biết tối thiểu về những gì nó được cho là không giúp ích).
Bob

Sẽ rất thú vị để xem làm thế nào điều này với Native NET blogs.msdn.com/b/dotnet/archive/2014/04/02/...
Rick Minerich

@Lembik Tôi vừa mới đi qua tất cả, theo như tôi có thể nói nó phải giống hệt với giải pháp Python khác ... bây giờ tôi thực sự bối rối.
Bob

4

Haskell: ~ 2000x tăng tốc cho mỗi lõi

Biên dịch với 'ghc -O3 -funbox -rict-field -threaded -fllvm' và chạy với '+ RTS -Nk' trong đó k là số lõi trên máy của bạn.

import Control.Parallel.Strategies
import Data.Bits
import Data.List
import Data.Word
import System.Random

n = 6 :: Int
iters = 1000 :: Int

data G = G !Word !Word !Word !Word deriving (Eq, Show)

gen :: G -> (Word, G)
gen (G x y z w) = let t  = x `xor` (x `shiftL` 11)
                      w' = w `xor` (w `shiftR` 19) `xor` t `xor` (t `shiftR` 8)
                  in (w', G y z w w')  

mask :: Word -> Word
mask = (.&.) $ (2 ^ n) - 1

gen_nonzero :: G -> (Word, G)
gen_nonzero g = let (x, g') = gen g 
                    a = mask x
                in if a == 0 then gen_nonzero g' else (a, g')


data F = F {zeros  :: !Word, 
            posneg :: !Word} deriving (Eq, Show)

gen_f :: G -> (F, G)       
gen_f g = let (a, g')  = gen_nonzero g
              (b, g'') = gen g'
          in  (F a $ mask b, g'')

inner :: Word -> F -> Int
inner s (F zs pn) = let s' = complement $ s `xor` pn
                        ones = s' .&. zs
                        negs = (complement s') .&. zs
                    in popCount ones - popCount negs

specialised_convolve :: Word -> F -> (Int, Int)
specialised_convolve s f@(F zs pn) = (inner s f', inner s f) 
    where f' = F (zs `shiftL` 1) (pn `shiftL` 1)

ss :: [Word]
ss = [0..2 ^ (n + 1) - 1]

main_loop :: [G] -> (Int, Int)
main_loop gs = foldl1' (\(fz, bz) (fz', bz') -> (fz + fz', bz + bz')) . parMap rdeepseq helper $ zip ss gs
    where helper (s, g) = go 0 (0, 0) g
                where go k u@(fz, bz) g = if k == iters 
                                              then u 
                                              else let (f, g') = gen_f g
                                                       v = case specialised_convolve s f
                                                               of (0, 0) -> (fz + 1, bz + 1)
                                                                  (0, _) -> (fz + 1, bz)
                                                                  _      -> (fz, bz)
                                                   in go (k + 1) v g'

seed :: IO G                                        
seed = do std_g <- newStdGen
          let [x, y, z, w] = map fromIntegral $ take 4 (randoms std_g :: [Int])
          return $ G x y z w

main :: IO ()
main = (sequence $ map (const seed) ss) >>= print . main_loop

2
Vậy với 4 nhân thì hơn 9000 ?! Không có cách nào có thể đúng.
Cees Timmerman

Định luật Amdahl tuyên bố việc tăng tốc độ song song không tuyến tính với số lượng đơn vị xử lý song song. thay vào đó họ chỉ cung cấp lợi nhuận
giảm dần

@xaedes Việc tăng tốc về cơ bản là tuyến tính với số lượng lõi thấp
user1502040

3

Hồng ngọc

Ruby (2.1.0) 0.277s
Ruby (2.1.1) 0.281s
Python (Alistair Buxton) 0.330s
Python (alemi) 0.097s

n = 6
iters = 1000
first_zero = 0
both_zero = 0

choices = [-1, 0, 0, 1].repeated_permutation(n).select{|v| [0] != v.uniq}

def convolve(v1, v2)
  [0, 1].map do |i|
    r = 0
    6.times do |j|
      r += v1[i+j] * v2[j]
    end
    r
  end
end

[-1, 1].repeated_permutation(n+1) do |s|
  iters.times do
    f = choices.sample
    fs = convolve s, f
    if 0 == fs[0]
      first_zero += 1
      if 0 == fs[1]
        both_zero += 1
      end
    end
  end
end

puts 'firstzero %i' % first_zero
puts 'bothzero %i' % both_zero

3

chủ đề sẽ không được hoàn thành mà không có PHP

Nhanh hơn 6,6 lần

PHP v5.5.9 - 1.223 0.646 giây;

đấu với

Python v2.7.6 - 8.072 giây

<?php

$n = 6;
$iters = 1000;
$firstzero = 0;
$bothzero = 0;

$x=time();
$y=34353;
$z=57768;
$w=1564; //PRNG seeds

function myRand() {
    global $x;
    global $y;
    global $z;
    global $w;
    $t = $x ^ ($x << 11);
    $x = $y; $y = $z; $z = $w;
    return $w = $w ^ ($w >> 19) ^ $t ^ ($t >> 8);
}

function array_cartesian() {
    $_ = func_get_args();
    if (count($_) == 0)
        return array();
    $a = array_shift($_);
    if (count($_) == 0)
        $c = array(array());
    else
        $c = call_user_func_array(__FUNCTION__, $_);
    $r = array();
    foreach($a as $v)
        foreach($c as $p)
            $r[] = array_merge(array($v), $p);
    return $r;
}

function rand_array($a, $n)
{
    $r = array();
    for($i = 0; $i < $n; $i++)
        $r[] = $a[myRand()%count($a)];
    return $r;
}

function convolve($a, $b)
{
    // slows down
    /*if(count($a) < count($b))
        return convolve($b,$a);*/
    $result = array();
    $w = count($a) - count($b) + 1;
    for($i = 0; $i < $w; $i++){
        $r = 0;
        for($k = 0; $k < count($b); $k++)
            $r += $b[$k] * $a[$i + $k];
        $result[] = $r;
    }
    return $result;
}

$cross = call_user_func_array('array_cartesian',array_fill(0,$n+1,array(-1,1)));

foreach($cross as $S)
    for($i = 0; $i < $iters; $i++){
        while(true)
        {
            $F = rand_array(array(-1,0,0,1), $n);
            if(in_array(-1, $F) || in_array(1, $F))
                break;
        }
        $FS = convolve($S, $F);
        if(0==$FS[0]) $firstzero += 1;
        if(0==$FS[0] && 0==$FS[1]) $bothzero += 1;
    }

echo "firstzero $firstzero\n";
echo "bothzero $bothzero\n";
  • Đã sử dụng một trình tạo ngẫu nhiên tùy chỉnh (bị đánh cắp từ câu trả lời C), PHP một lần hút và các số không khớp
  • convolve chức năng đơn giản hóa một chút để nhanh hơn
  • Việc kiểm tra chỉ với các số không cũng được tối ưu hóa (xem $F$FSkiểm tra).

Đầu ra:

$ time python num.py 
firstzero 27050
bothzero 11990

real    0m8.072s
user    0m8.037s
sys 0m0.024s
$ time php num.php
firstzero 27407
bothzero 12216

real    0m1.223s
user    0m1.210s
sys 0m0.012s

Biên tập. Phiên bản thứ hai của tập lệnh chỉ hoạt động 0.646 sec:

<?php

$n = 6;
$iters = 1000;
$firstzero = 0;
$bothzero = 0;

$x=time();
$y=34353;
$z=57768;
$w=1564; //PRNG seeds

function myRand() {
    global $x;
    global $y;
    global $z;
    global $w;
    $t = $x ^ ($x << 11);
    $x = $y; $y = $z; $z = $w;
    return $w = $w ^ ($w >> 19) ^ $t ^ ($t >> 8);
}

function array_cartesian() {
    $_ = func_get_args();
    if (count($_) == 0)
        return array();
    $a = array_shift($_);
    if (count($_) == 0)
        $c = array(array());
    else
        $c = call_user_func_array(__FUNCTION__, $_);
    $r = array();
    foreach($a as $v)
        foreach($c as $p)
            $r[] = array_merge(array($v), $p);
    return $r;
}

function convolve($a, $b)
{
    // slows down
    /*if(count($a) < count($b))
        return convolve($b,$a);*/
    $result = array();
    $w = count($a) - count($b) + 1;
    for($i = 0; $i < $w; $i++){
        $r = 0;
        for($k = 0; $k < count($b); $k++)
            $r += $b[$k] * $a[$i + $k];
        $result[] = $r;
    }
    return $result;
}

$cross = call_user_func_array('array_cartesian',array_fill(0,$n+1,array(-1,1)));

$choices = call_user_func_array('array_cartesian',array_fill(0,$n,array(-1,0,0,1)));

foreach($cross as $S)
    for($i = 0; $i < $iters; $i++){
        while(true)
        {
            $F = $choices[myRand()%count($choices)];
            if(in_array(-1, $F) || in_array(1, $F))
                break;
        }
        $FS = convolve($S, $F);
        if(0==$FS[0]){
            $firstzero += 1;
            if(0==$FS[1])
                $bothzero += 1;
        }
    }

echo "firstzero $firstzero\n";
echo "bothzero $bothzero\n";

3

Giải pháp F #

Thời gian chạy là 0,030 giây khi được biên dịch thành x86 trên CLR Core i7 4 (8) @ 3,4 Ghz

Tôi không có ý tưởng nếu mã là chính xác.

  • Tối ưu hóa chức năng (gấp nội tuyến) -> 0,026s
  • Xây dựng thông qua Dự án Console -> 0,022s
  • Đã thêm một thuật toán tốt hơn để tạo các mảng hoán vị -> 0,008s
  • Mono cho Windows -> 0,089s
  • Chạy tập lệnh Python của Alistair -> 0.259s
let inline ffoldi n f state =
    let mutable state = state
    for i = 0 to n - 1 do
        state <- f state i
    state

let product values n =
    let p = Array.length values
    Array.init (pown p n) (fun i ->
        (Array.zeroCreate n, i)
        |> ffoldi n (fun (result, i') j ->
            result.[j] <- values.[i' % p]
            result, i' / p
        )
        |> fst
    )

let convolute signals filter =
    let m = Array.length signals
    let n = Array.length filter
    let len = max m n - min m n + 1

    Array.init len (fun offset ->
        ffoldi n (fun acc i ->
            acc + filter.[i] * signals.[m - 1 - offset - i]
        ) 0
    )

let n = 6
let iters = 1000

let next =
    let arrays =
        product [|-1; 0; 0; 1|] n
        |> Array.filter (Array.forall ((=) 0) >> not)
    let rnd = System.Random()
    fun () -> arrays.[rnd.Next arrays.Length]

let signals = product [|-1; 1|] (n + 1)

let firstzero, bothzero =
    ffoldi signals.Length (fun (firstzero, bothzero) i ->
        let s = signals.[i]
        ffoldi iters (fun (first, both) _ ->
            let f = next()
            match convolute s f with
            | [|0; 0|] -> first + 1, both + 1
            | [|0; _|] -> first + 1, both
            | _ -> first, both
        ) (firstzero, bothzero)
    ) (0, 0)

printfn "firstzero %i" firstzero
printfn "bothzero %i" bothzero

2

Q, 0,296

n:6; iter:1000  /parametrization (constants)
c:n#0           /auxiliar constant (sequence 0 0.. 0 (n))
A:B:();         /A and B accumulates results of inner product (firstresult, secondresult)

/S=sequence with all arrays of length n+1 with values -1 and 1
S:+(2**m)#/:{,/x#/:-1 1}'m:|n(2*)\1 

f:{do[iter; F:c; while[F~c; F:n?-1 0 0 1]; A,:+/F*-1_x; B,:+/F*1_x];} /hard work
f'S               /map(S,f)
N:~A; +/'(N;N&~B) / ~A is not A (or A=0) ->bitmap.  +/ is sum (population over a bitmap)
                  / +/'(N;N&~B) = count firstResult=0, count firstResult=0 and secondResult=0

Q là ngôn ngữ hướng bộ sưu tập (kx.com)

Mã được viết lại để khám phá thành ngữ Q, nhưng không có tối ưu hóa thông minh nào khác

Ngôn ngữ script tối ưu hóa thời gian lập trình viên, không phải thời gian thực hiện

  • Q không phải là công cụ tốt nhất cho vấn đề này

Lần thử mã hóa đầu tiên = không phải là người chiến thắng, nhưng thời gian hợp lý (khoảng 30 lần tăng tốc)

  • khá cạnh tranh giữa các phiên dịch viên
  • dừng lại và chọn một vấn đề khác

GHI CHÚ

  • chương trình sử dụng hạt giống mặc định (thực thi lặp lại) Để chọn hạt giống khác cho sử dụng trình tạo ngẫu nhiên \S seed
  • Kết quả được đưa ra dưới dạng một nhóm gồm hai số nguyên, vì vậy có một hậu tố i cuối cùng ở giá trị thứ hai 27421 12133i -> đọc là (27241, 12133)
  • Thời gian không tính khởi động phiên dịch. \t sentence mesures tiêu tốn thời gian của câu đó

Rất thú vị cảm ơn bạn.

1

Julia: 12.149 6,929 giây

Mặc dù yêu cầu của họ về tốc độ , thời gian biên dịch JIT ban đầu giữ chúng tôi lại!

Lưu ý rằng mã Julia sau đây thực sự là bản dịch trực tiếp mã Python gốc (không thực hiện tối ưu hóa) như một minh chứng rằng bạn có thể dễ dàng chuyển trải nghiệm lập trình của mình sang ngôn ngữ nhanh hơn;)

require("Iterators")

n = 6
iters = 1000
firstzero = 0
bothzero = 0

for S in Iterators.product(fill([-1,1], n+1)...)
    for i = 1:iters
        F = [[-1 0 0 1][rand(1:4)] for _ = 1:n]
        while all((x) -> round(x,8) == 0, F)
            F = [[-1 0 0 1][rand(1:4)] for _ = 1:n]
        end
        FS = conv(F, [S...])
        if round(FS[1],8) == 0
            firstzero += 1
        end
        if all((x) -> round(x,8) == 0, FS)
            bothzero += 1
        end
    end
end

println("firstzero ", firstzero)
println("bothzero ", bothzero)

Biên tập

Chạy với n = 8mất 32.935 giây. Xét rằng sự phức tạp của thuật toán này là O(2^n), sau đó 4 * (12.149 - C) = (32.935 - C), nơi Clà một hằng số đại diện cho thời gian biên dịch JIT. Giải quyết cho Cchúng tôi thấy rằng C = 5.2203, cho thấy thời gian thực hiện thực tế n = 6là 6,929 giây.


Làm thế nào về việc tăng n lên 8 để xem Julia có trở thành của riêng mình không?

Điều này bỏ qua nhiều lời khuyên về hiệu suất ở đây: julia.readthedocs.org/en/latest/manual/performance-tips . Xem thêm các mục khác của Julia mà tốt hơn đáng kể. Việc gửi được đánh giá cao mặc dù :-)
StefanKarpinski

0

Rust, 6,6 ms, tăng tốc 1950x

Khá nhiều bản dịch trực tiếp mã của Alistair Buxton sang Rust. Tôi đã cân nhắc việc sử dụng nhiều lõi với rayon (đồng thời không sợ hãi!), Nhưng điều này không cải thiện hiệu suất, có lẽ vì nó đã rất nhanh.

extern crate itertools;
extern crate rand;
extern crate time;

use itertools::Itertools;
use rand::{prelude::*, prng::XorShiftRng};
use std::iter;
use time::precise_time_ns;

fn main() {
    let start = precise_time_ns();

    let n = 6;
    let iters = 1000;
    let mut first_zero = 0;
    let mut both_zero = 0;
    let choices_f: Vec<Vec<i8>> = iter::repeat([-1, 0, 0, 1].iter().cloned())
        .take(n)
        .multi_cartesian_product()
        .filter(|i| i.iter().any(|&x| x != 0))
        .collect();
    // xorshift RNG is faster than default algorithm designed for security
    // rather than performance.
    let mut rng = XorShiftRng::from_entropy(); 
    for s in iter::repeat(&[-1, 1]).take(n + 1).multi_cartesian_product() {
        for _ in 0..iters {
            let f = rng.choose(&choices_f).unwrap();
            if f.iter()
                .zip(&s[..s.len() - 1])
                .map(|(a, &b)| a * b)
                .sum::<i8>() == 0
            {
                first_zero += 1;
                if f.iter().zip(&s[1..]).map(|(a, &b)| a * b).sum::<i8>() == 0 {
                    both_zero += 1;
                }
            }
        }
    }
    println!("first_zero = {}\nboth_zero = {}", first_zero, both_zero);

    println!("runtime {} ns", precise_time_ns() - start);
}

Và Cargo.toml, khi tôi sử dụng các phụ thuộc bên ngoài:

[package]
name = "how_slow_is_python"
version = "0.1.0"

[dependencies]
itertools = "0.7.8"
rand = "0.5.3"
time = "0.1.40"

So sánh tốc độ:

$ time python2 py.py
firstzero: 27478
bothzero: 12246
12.80user 0.02system 0:12.90elapsed 99%CPU (0avgtext+0avgdata 23328maxresident)k
0inputs+0outputs (0major+3544minor)pagefaults 0swaps
$ time target/release/how_slow_is_python
first_zero = 27359
both_zero = 12162
runtime 6625608 ns
0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 2784maxresident)k
0inputs+0outputs (0major+189minor)pagefaults 0swaps

6625608 ns là khoảng 6,6 ms. Điều này có nghĩa là tăng tốc 1950 lần. Có nhiều tối ưu hóa có thể có ở đây, nhưng tôi sẽ dễ đọc hơn là hiệu năng. Một tối ưu hóa có thể sẽ là sử dụng mảng thay vì vectơ để lưu trữ các lựa chọn, vì chúng sẽ luôn có ncác phần tử. Cũng có thể sử dụng RNG khác với XorShift, vì trong khi Xorshift nhanh hơn HC-128 CSPRNG mặc định, nó chậm nhất so với các thuật toán PRNG ngây thơ.

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.