Helpex - Trao đổi & giúp đỡ Đăng nhập
18

Này, bạn bè của tôi và tôi đang cố gắng đánh bại thời gian chạy của nhau để tạo ra " Số tự " từ 1 đến một triệu. Tôi đã viết của tôi bằng c ++ và tôi vẫn đang cố gắng dành thời gian quý báu.

Đây là những gì tôi có cho đến nay,

#include <iostream>

using namespace std;

bool v[1000000];
int main(void) {
  long non_self = 0;
  for(long i = 1; i < 1000000; ++i) {
    if(!(v[i])) std::cout << i << '\n';
    non_self = i + (i%10) + (i/10)%10 + (i/100)%10 + (i/1000)%10 + (i/10000)%10 +(i/100000)%10;
    v[non_self] = 1;
  }
  std::cout << "1000000" << '\n';
  return 0;
}

Hiện tại, mã hoạt động tốt, tôi chỉ muốn tối ưu hóa nó. Có lời khuyên nào không? Cảm ơn.

18 hữu ích 5 bình luận 7.4k xem chia sẻ
15 trả lời 15
27

Tôi đã tạo một giải pháp C thay thế không yêu cầu bất kỳ mô đun hoặc phép toán phân chia nào:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
   int v[1100000];
   int j1, j2, j3, j4, j5, j6, s, n=0;
   memset(v, 0, sizeof(v));
   for (j6=0; j6<10; j6++) {
      for (j5=0; j5<10; j5++) {
         for (j4=0; j4<10; j4++) {
            for (j3=0; j3<10; j3++) {
               for (j2=0; j2<10; j2++) {
                  for (j1=0; j1<10; j1++) {
                     s = j6 + j5 + j4 + j3 + j2 + j1;
                     v[n + s] = 1;
                     n++;
                  }
               }
            }
         }
      }
   }
   for (n=1; n<=1000000; n++) {
      if (!v[n]) printf("%6d\n", n);
   }
}

Nó tạo ra 97786 số tự bao gồm 1 và 1000000.
Với đầu ra, phải

real        0m1.419s
user        0m0.060s
sys         0m0.152s

Khi tôi chuyển hướng đầu ra đến / dev / null, nó sẽ mất

real     0m0.030s
user     0m0.024s
sys      0m0.004s

trên giàn lõi tứ 3 Ghz của tôi.

Để so sánh, phiên bản của bạn tạo ra cùng một số lượng, vì vậy tôi cho rằng chúng tôi đều đúng hoặc đều sai; nhưng phiên bản của bạn nhai lại

real    0m0.064s
user    0m0.060s
sys     0m0.000s

trong cùng điều kiện, hoặc khoảng gấp đôi.

Điều đó, hoặc thực tế là bạn đang sử dụng longs, thứ không cần thiết trên máy của tôi. Ở đây, intlên đến 2 tỷ đồng. Có lẽ bạn nên kiểm tra INT_MAXcủa bạn?

Cập nhật

Tôi có linh cảm rằng có thể tốt hơn nếu tính tổng từng mảnh. Đây là mã mới của tôi:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
   char v[1100000];
   int j1, j2, j3, j4, j5, j6, s, n=0;
   int s1, s2, s3, s4, s5;
   memset(v, 0, sizeof(v));
   for (j6=0; j6<10; j6++) {
      for (j5=0; j5<10; j5++) {
         s5 = j6 + j5;
         for (j4=0; j4<10; j4++) {
            s4 = s5 + j4;
            for (j3=0; j3<10; j3++) {
               s3 = s4 + j3;
               for (j2=0; j2<10; j2++) {
                  s2 = s3 + j2;
                  for (j1=0; j1<10; j1++) {
                     v[s2 + j1 + n++] = 1;
                  }
               }
            }
         }
      }
   }
   for (n=1; n<=1000000; n++) {
      if (!v[n]) printf("%d\n", n);
   }
}

... và bạn biết gì không, điều đó đã làm giảm thời gian cho vòng lặp trên cùng từ 12 mili giây xuống còn 4 mili giây. Hoặc có thể là số 8, đồng hồ của tôi dường như đang trở nên hơi chập chờn ở phía dưới đó.

Tình hình sự việc, Tóm tắt

Việc tìm kiếm thực tế các số tự lên đến 1 triệu hiện đang mất khoảng 4 mili giây và tôi đang gặp sự cố khi đo lường bất kỳ cải tiến nào nữa. Mặt khác, miễn là đầu ra đến bảng điều khiển, nó sẽ tiếp tục mất khoảng 1,4 giây, tôi cố gắng hết sức để tận dụng bộ đệm. Thời gian I / O rút ngắn đáng kể thời gian tính toán đến mức bất kỳ tối ưu hóa nào về cơ bản sẽ trở nên vô ích. Vì vậy, mặc dù được truyền cảm hứng bởi những bình luận khác, tôi đã quyết định để yên.

Tất cả thời gian được trích dẫn đều trên máy (khá nhanh) của tôi và chỉ dành cho mục đích so sánh với nhau. Số dặm của bạn có thể thay đổi.

27 hữu ích 5 bình luận chia sẻ
13

Tạo các số một lần, sao chép đầu ra vào mã của bạn dưới dạng một chuỗi khổng lồ. In chuỗi.

13 hữu ích 3 bình luận chia sẻ
13

Những mod đó ( %) trông đắt tiền. Nếu bạn được phép chuyển sang cơ sở 16 (hoặc thậm chí là cơ sở 2), thì bạn có thể viết mã này nhanh hơn rất nhiều. Nếu bạn phải ở dạng thập phân, hãy thử tạo một mảng chữ số cho mỗi vị trí (đơn vị, hàng chục, hàng trăm) và xây dựng một số mã di chuột qua. Điều đó sẽ làm cho việc tính tổng các con số trở nên dễ dàng hơn rất nhiều.


Ngoài ra, bạn có thể nhận ra hoạt động của chức năng tự cốt lõi (chúng ta hãy gọi nó s):

s = n + f(b,n)

ở đâu f(b,n)là tổng các chữ số của số ntrong cơ số b.

Đối với cơ số 10, rõ ràng là khi các chữ số hàng đơn vị (còn được gọi là ít có ý nghĩa nhất) di chuyển từ 0,1,2, ..., 9, nf(b,n)tiến hành trong bước khóa khi bạn chuyển từ nsang n+1, thì chỉ có 10% thời gian mà 9 cuộn đến 0 mà nó không, do đó:

f(b,n+1) = f(b,n) + 1  // 90% of the time

do đó, chức năng tự cốt lõi stiến bộ như

n+1 + f(b,n+1) = n + 1 + f(b,n) + 1 = n + f(b,n) + 2

s(n+1) = s(n) + 2 // again, 90% of the time

Trong 10% thời gian còn lại (và có thể nhận dạng dễ dàng), số 9 quay trở lại số 0 và thêm một vào chữ số tiếp theo, trong trường hợp đơn giản nhất, số này trừ (9-1) khỏi tổng số đang chạy, nhưng có thể tăng lên thông qua một chuỗi số 9, để trừ 99-1, 999-1, v.v.

Vì vậy, lần tối ưu hóa đầu tiên có thể loại bỏ hầu hết công việc khỏi 90% chu kỳ của bạn!

if ((n % 10) != 0) 
{
  n + f(b,n) = n-1 + f(b,n-1) + 2;
}

hoặc là

if ((n % 10) != 0)
{
  s = old_s + 2;
}

Điều đó đủ để tăng đáng kể hiệu suất của bạn mà không thực sự thay đổi thuật toán của bạn.

Nếu bạn muốn nhiều hơn, hãy tính toán một thuật toán đơn giản cho sự thay đổi giữa các lần lặp cho 10% còn lại.

13 hữu ích 0 bình luận chia sẻ
5

Nếu bạn muốn đầu ra của mình nhanh, bạn nên điều tra thay thế đầu ra iostream bằng printf () cũ thuần túy - phụ thuộc vào các quy tắc để giành chiến thắng trong cuộc thi cho dù điều này có quan trọng hay không.

5 hữu ích 0 bình luận chia sẻ
3

Đa luồng (sử dụng các mảng / phạm vi khác nhau cho mọi luồng). Ngoài ra, không sử dụng nhiều luồng hơn số lõi cpu của bạn =)

3 hữu ích 2 bình luận chia sẻ
3

cout hoặc printf trong một vòng lặp sẽ bị chậm. Nếu bạn có thể loại bỏ bất kỳ bản in nào khỏi một vòng lặp, bạn sẽ thấy hiệu suất tăng lên đáng kể.

3 hữu ích 0 bình luận chia sẻ
3

Vì phạm vi bị giới hạn (1 đến 1000000) nên tổng các chữ số tối đa không vượt quá 9 * 6 = 54. Điều này có nghĩa là để thực hiện sàng, một bộ đệm tròn gồm 54 phần tử phải hoàn toàn đủ (và kích thước của sàng lớn lên rất chậm khi phạm vi tăng lên).

Bạn đã có một giải pháp dựa trên sàng, nhưng nó dựa trên việc xây dựng trước bộ đệm có chiều dài đầy đủ (sàng 1000000 phần tử), điều này khá không phù hợp (nếu không muốn nói là hoàn toàn không thể chấp nhận được). Hiệu suất của giải pháp của bạn cũng gặp phải tình trạng truy cập bộ nhớ không định vị.

Ví dụ, đây là một triển khai rất đơn giản có thể thực hiện được

#define N 1000000U

void print_self_numbers(void)
{
  #define NMARKS 64U /* make it 64 just in case (and to make division work faster :) */

  unsigned char marks[NMARKS] = { 0 };
  unsigned i, imark;

  for (i = 1, imark = i; i <= N; ++i, imark = (imark + 1) % NMARKS)
  {
    unsigned digits, sum;

    if (!marks[imark])
      printf("%u ", i);
    else
      marks[imark] = 0;

    sum = i;
    for (digits = i; digits > 0; digits /= 10)
      sum += digits % 10;

    marks[sum % NMARKS] = 1;
  }
}

(Tôi sẽ không tìm kiếm hiệu suất tốt nhất có thể về xung nhịp CPU ở đây, chỉ minh họa ý tưởng chính với bộ đệm tròn.)

Tất nhiên, phạm vi có thể dễ dàng được biến thành một tham số của hàm, trong khi kích thước của bộ đệm cong có thể dễ dàng được tính toán tại thời gian chạy từ phạm vi.

Đối với "tối ưu hóa" ... Chẳng ích gì khi cố gắng tối ưu hóa mã chứa các hoạt động I / O. Bạn sẽ không đạt được bất cứ điều gì bằng cách tối ưu hóa như vậy. Nếu bạn muốn phân tích hiệu suất của chính thuật toán, bạn sẽ phải đặt các số đã tạo vào một mảng đầu ra và in chúng sau đó.

3 hữu ích 0 bình luận chia sẻ
1

Đối với nhiệm vụ đơn giản như vậy, lựa chọn tốt nhất là nghĩ đến các thuật toán thay thế để tạo ra cùng một kết quả. % 10 thường không được coi là một hoạt động nhanh.

1 hữu ích 1 bình luận chia sẻ
1

Thay vào đó, tại sao không sử dụng quan hệ lặp lại được đưa ra trên trang wikipedia? Điều đó sẽ rất nhanh.

CHỈNH SỬA: Bỏ qua điều này .. quan hệ lặp lại tạo ra một số nhưng không phải tất cả các số tự. Trong thực tế, chỉ có rất ít trong số họ. Tuy nhiên, điều đó không rõ ràng đặc biệt từ trang thewikipedia :(

1 hữu ích 3 bình luận chia sẻ
1

Điều này có thể giúp tăng tốc đầu ra C ++ iostreams:

cin.tie(0);
ios::sync_with_stdio(false);

Đặt chúng vào chính trước khi bạn bắt đầu viết để cout.

1 hữu ích 0 bình luận chia sẻ
1

Tôi đã tạo một giải pháp dựa trên CUDA dựa trên thuật toán thứ hai của Carl Smotricz. Bản thân mã xác định Số tự cực kỳ nhanh - trên máy của tôi, nó thực thi trong ~ 45 nano giây; tốc độ này nhanh hơn khoảng 150 lần so với thuật toán của Carl Smotricz, chạy trong 7 mili giây trên máy của tôi.

Tuy nhiên, có một điểm nghẽn và đó dường như là giao diện PCIe. Mã của tôi mất tới 43 mili giây khổng lồ để di chuyển dữ liệu được tính toán từ cạc đồ họa trở lại RAM. Điều này có thể được tối ưu hóa, và tôi sẽ xem xét điều này.

Tuy nhiên, 45 nanosedon là khá nhanh. Thực sự là nhanh đáng sợ và tôi đã thêm mã vào chương trình của mình, chương trình chạy thuật toán của Carl Smotricz và so sánh kết quả cho chính xác. Kết quả là chính xác. Đây là đầu ra của chương trình (được biên dịch trong VS2008 64-bit, Windows7):

CẬP NHẬT

Tôi đã biên dịch lại mã này ở chế độ phát hành với tối ưu hóa hoàn toàn và sử dụng thư viện thời gian chạy tĩnh, với kết quả đáng kể. Trình tối ưu hóa dường như đã làm rất tốt với thuật toán của Carl, giảm thời gian chạy từ 7 ms xuống 1 ms. Việc triển khai CUDA cũng tăng nhanh, từ 35 chúng tôi lên 20 chúng tôi. Bản sao bộ nhớ từ thẻ video sang RAM không bị ảnh hưởng.

Đầu ra chương trình:

Running on device: 'Quadro NVS 295'
Reference Implementation Ran In 15603 ticks (7 ms)
Kernel Executed in 40 ms -- Breakdown:
  [kernel] : 35 us (0.09%)
  [memcpy] : 40 ms (99.91%)
CUDA Implementation Ran In 111889 ticks (51 ms)
Compute Slots: 1000448 (1954 blocks X 512 threads)
Number of Errors: 0

Mã như sau:

tệp: main.h

#pragma once

#include <cstdlib>
#include <functional>

typedef std::pair<int*, size_t> sized_ptr;
static sized_ptr make_sized_ptr(int* ptr, size_t size)
{
    return make_pair<int*, size_t>(ptr, size);
}

__host__ void ComputeSelfNumbers(sized_ptr hostMem, sized_ptr deviceMemory, unsigned const blocks, unsigned const threads);

inline std::string format_elapsed(double d) 
{
    char buf[256] = {0};

    if( d < 0.00000001 )
    {
        // show in ps with 4 digits
        sprintf(buf, "%0.4f ps", d * 1000000000000.0);
    }
    else if( d < 0.00001 )
    {
        // show in ns
        sprintf(buf, "%0.0f ns", d * 1000000000.0);
    }
    else if( d < 0.001 )
    {
        // show in us
        sprintf(buf, "%0.0f us", d * 1000000.0);
    }
    else if( d < 0.1 )
    {
        // show in ms
        sprintf(buf, "%0.0f ms", d * 1000.0);
    }
    else if( d <= 60.0 )
    {
        // show in seconds
        sprintf(buf, "%0.2f s", d);
    }
    else if( d < 3600.0 )
    {
        // show in min:sec
        sprintf(buf, "%01.0f:%02.2f", floor(d/60.0), fmod(d,60.0));
    }
    // show in h:min:sec
    else 
        sprintf(buf, "%01.0f:%02.0f:%02.2f", floor(d/3600.0), floor(fmod(d,3600.0)/60.0), fmod(d,60.0));

    return buf;
}

inline std::string format_pct(double d)
{
    char buf[256] = {0};
    sprintf(buf, "%.2f", 100.0 * d);
    return buf;
}

tệp: main.cpp

#define _CRT_SECURE_NO_WARNINGS 

#include <windows.h>
#include "C:\CUDA\include\cuda_runtime.h"
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
#include <cmath>
#include <map>
#include <algorithm>
#include <list>

#include "main.h"

int main()
{
    unsigned numVals = 1000000;
    int* gold = new int[numVals];
    memset(gold, 0, sizeof(int)*numVals);

    LARGE_INTEGER li = {0}, li2 = {0};
    QueryPerformanceFrequency(&li);
    __int64 freq = li.QuadPart;

    // get cuda properties...
    cudaDeviceProp cdp = {0};
    cudaError_t err = cudaGetDeviceProperties(&cdp, 0);
cout << "Running on device: '" << cdp.name << "'" << endl;

    // first run the reference implementation
    QueryPerformanceCounter(&li);
    for( int j6=0, n = 0; j6<10; j6++ ) 
    {
        for( int j5=0; j5<10; j5++ ) 
        {
            for( int j4=0; j4<10; j4++ ) 
            {
                for( int j3=0; j3<10; j3++ ) 
                {
                    for( int j2=0; j2<10; j2++ ) 
                    {
                        for( int j1=0; j1<10; j1++ )  
                        {
                            int s = j6 + j5 + j4 + j3 + j2 + j1;
                            gold[n + s] = 1;
                            n++;
                        }
                    }
                }
            }
        }
    }
    QueryPerformanceCounter(&li2);
    __int64 ticks = li2.QuadPart-li.QuadPart;
    cout << "Reference Implementation Ran In " << ticks << " ticks" << " (" << format_elapsed((double)ticks/(double)freq) << ")" << endl;

    // now run the cuda version...
    unsigned threads = cdp.maxThreadsPerBlock;
    unsigned blocks = numVals/threads;
    if( numVals%threads ) ++blocks;
    unsigned computeSlots = blocks * threads;   // this may be != the number of vals since we want 32-thread warps

    // allocate device memory for test
    int* deviceTest = 0;
    err = cudaMalloc(&deviceTest, sizeof(int)*computeSlots);
    err = cudaMemset(deviceTest, 0, sizeof(int)*computeSlots);

    int* hostTest = new int[numVals];   // the repository for the resulting data on the host
    memset(hostTest, 0, sizeof(int)*numVals);

    // run the CUDA code...
    LARGE_INTEGER li3 = {0}, li4={0};
    QueryPerformanceCounter(&li3);
    ComputeSelfNumbers(make_sized_ptr(hostTest, numVals), make_sized_ptr(deviceTest, computeSlots), blocks, threads);
    QueryPerformanceCounter(&li4);

    __int64 ticksCuda = li4.QuadPart-li3.QuadPart;
    cout << "CUDA Implementation Ran In " << ticksCuda << " ticks" << " (" << format_elapsed((double)ticksCuda/(double)freq) << ")" << endl;
    cout << "Compute Slots: " << computeSlots << " (" << blocks << " blocks X " << threads << " threads)" << endl;


    unsigned errorCount = 0;
    for( size_t i = 0; i < numVals; ++i )
    {
        if( gold[i] != hostTest[i] )
        {
            ++errorCount;
        }
    }

    cout << "Number of Errors: " << errorCount << endl;

    return 0;
}

tệp: self.cu

#pragma warning( disable : 4231)
#include <windows.h>
#include <cstdlib>
#include <vector>
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
#include "main.h"

__global__ void SelfNum(int * slots)
{
    __shared__ int N;
    N = (blockIdx.x * blockDim.x) + threadIdx.x;

    const int numDigits = 10;

    __shared__ int digits[numDigits];
    for( int i = 0, temp = N; i < numDigits; ++i, temp /= 10 )
    {
        digits[numDigits-i-1] = temp - 10 * (temp/10)      /*temp % 10*/;
    }

    __shared__ int s;
    s = 0;
    for( int i = 0; i < numDigits; ++i )
        s += digits[i];

    slots[N+s] = 1;
}

__host__ void ComputeSelfNumbers(sized_ptr hostMem, sized_ptr deviceMem, const unsigned  blocks, const unsigned threads)
{
    LARGE_INTEGER li = {0};
    QueryPerformanceFrequency(&li);
    double freq = (double)li.QuadPart;

    LARGE_INTEGER liStart = {0};
    QueryPerformanceCounter(&liStart);

    // run the kernel
    SelfNum<<<blocks, threads>>>(deviceMem.first);
    LARGE_INTEGER liKernel = {0};
    QueryPerformanceCounter(&liKernel);

    cudaMemcpy(hostMem.first, deviceMem.first, hostMem.second*sizeof(int), cudaMemcpyDeviceToHost); // dont copy the overflow - just throw it away
    LARGE_INTEGER liMemcpy = {0};
    QueryPerformanceCounter(&liMemcpy);

    // display performance stats
    double e = double(liMemcpy.QuadPart - liStart.QuadPart)/freq,
        eKernel = double(liKernel.QuadPart - liStart.QuadPart)/freq,
        eMemcpy = double(liMemcpy.QuadPart - liKernel.QuadPart)/freq;

    double pKernel = eKernel/e,
        pMemcpy = eMemcpy/e;

    cout << "Kernel Executed in " << format_elapsed(e) << " -- Breakdown: " << endl
        << "  [kernel] : " << format_elapsed(eKernel) << " (" << format_pct(pKernel) << "%)" << endl
        << "  [memcpy] : " << format_elapsed(eMemcpy) << " (" << format_pct(pMemcpy) << "%)" << endl;



}

CẬP NHẬT2:

Tôi đã cấu trúc lại việc triển khai CUDA của mình để cố gắng tăng tốc một chút. Tôi đã thực hiện việc này bằng cách giải nén các vòng lặp theo cách thủ công, sửa một số vấn đề sử dụng __shared__bộ nhớ có thể là lỗi và loại bỏ một số phần dư thừa.

Đầu ra của hạt nhân mới của tôi là:

Reference Implementation Ran In 69610 ticks (5 ms)
Kernel Executed in 2 ms -- Breakdown:
  [kernel] : 39 us (1.57%)
  [memcpy] : 2 ms (98.43%)
CUDA Implementation Ran In 62970 ticks (4 ms)
Compute Slots: 1000448 (1954 blocks X 512 threads)
Number of Errors: 0

Mã duy nhất tôi đã thay đổi là chính hạt nhân, vì vậy đó là tất cả những gì tôi sẽ đăng ở đây:

__global__ void SelfNum(int * slots)
{
    int N = (blockIdx.x * blockDim.x) + threadIdx.x;

    int s = 0;

    int temp = N;
    s += temp - 10 * (temp/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;
    s += temp - 10 * ((temp/=10)/10)      /*temp % 10*/;

    slots[N+s] = 1;
}
1 hữu ích 0 bình luận chia sẻ
0

Tôi tự hỏi liệu đa luồng có hữu ích không. Thuật toán này có vẻ như nó sẽ hỗ trợ tốt cho đa luồng. (Thử nghiệm của người nghèo về điều này: Tạo hai bản sao của chương trình và chạy chúng cùng một lúc. Nếu nó chạy trong ít hơn 200% thời gian, đa luồng có thể hữu ích).

0 hữu ích 1 bình luận chia sẻ
0

Tôi thực sự ngạc nhiên rằng mã dưới đây nhanh hơn bất kỳ mã nào khác được đăng ở đây. Tôi có thể đo nó sai, nhưng có lẽ nó sẽ giúp ích; hoặc ít nhất là thú vị.

#include <iostream>
#include <boost/progress.hpp>

class SelfCalc
{
private:
    bool    array[1000000];
    int     non_self;

public:
    SelfCalc()
    {
        memset(&array, 0, sizeof(array));
    }

    void operator()(const int i)
    {
        if (!(array[i]))
            std::cout << i << '\n';

        non_self = i + (i%10) + (i/10)%10 + (i/100)%10 + (i/1000)%10 + (i/10000)%10 +(i/100000)%10;
        array[non_self] = true;
    }
};

class IntIterator
{
private:
    int value;

public: 
    IntIterator(const int _value):value(_value){}

    int operator*(){ return value; } 
    bool operator!=(const IntIterator &v){ return value != v.value; }
    int operator++(){ return ++value; }
};

int main()
{
    boost::progress_timer t;

    SelfCalc selfCalc;
    IntIterator i(1), end(100000);

    std::for_each(i, end, selfCalc);

    std::cout << 100000 << std::endl;
    return 0;
}
0 hữu ích 0 bình luận chia sẻ
0

Bài toán vui. Vấn đề như đã nêu không chỉ rõ nó phải ở cơ sở nào. Tôi đã mày mò với nó một số và viết một phiên bản cơ sở 2. Nó tạo ra thêm vài nghìn mục nhập vì điểm kết thúc 1.000.000 không tự nhiên với cơ số 2. Điều này đếm trước số bit trong một byte để tra cứu bảng. Quá trình tạo tập kết quả (không có I / O) mất 2,4 mili giây.

Một điều thú vị (giả sử tôi đã viết đúng) là phiên bản cơ sở 2 có khoảng 250.000 "số tự" lên đến 1.000.000 trong khi chỉ có dưới 100.000 số tự cơ sở 10 trong phạm vi đó.

#include <windows.h>
#include <stdio.h>
#include <string.h>

void StartTimer( _int64 *pt1 )
{
   QueryPerformanceCounter( (LARGE_INTEGER*)pt1 );
}

double StopTimer( _int64 t1 )
{
   _int64 t2, ldFreq;

   QueryPerformanceCounter( (LARGE_INTEGER*)&t2 );
   QueryPerformanceFrequency( (LARGE_INTEGER*)&ldFreq );
   return ((double)( t2 - t1 ) / (double)ldFreq) * 1000.0;
}

#define RANGE 1000000

char sn[0x100000 + 32];

int bitCount[256];

 // precompute bitcounts for each byte
void PreCountBits()
{
    int i;

    // generate count of bits in each byte
    memset( bitCount, 0, sizeof( bitCount ));
    for ( i = 0; i < 256; i++ )
        {
        int tmp = i;

        while ( tmp )
            {
            if ( tmp & 0x01 )
                bitCount[i]++;
            tmp >>= 1;
            }
        }
}


void GenBase2( )
{
    int i;
    int *b1, *b2, *b3;
    int b1sum, b2sum, b3sum;

    i = 0;
    for ( b1 = bitCount; b1 < bitCount + 256; b1++ )
        {
        b1sum = *b1;
        for ( b2 = bitCount; b2 < bitCount + 256; b2++ )
            {
            b2sum = b1sum + *b2;
            for ( b3 = bitCount; b3 < bitCount + 256; b3++ )
                {
                sn[i++ + *b3 + b2sum] = 1;
                }
            }

        // 1000000 does not provide a great termination number for base 2.  So check
        // here.  Overshoots the target some but avoids repeated checks
        if ( i > RANGE )
            return;
        }
}


int main( int argc, char* argv[] )
{
    int i = 0;
    __int64 t1;


    memset( sn, 0, sizeof( sn ));
    StartTimer( &t1 );
    PreCountBits();
    GenBase2();
    printf( "Generation time = %.3f\n", StopTimer( t1 ));

    #if 1
    for ( i = 1; i <= RANGE; i++ )
        if ( !sn[i] ) printf( "%d\n", i );
    #endif
    return 0;
}
0 hữu ích 0 bình luận chia sẻ
-1

Có thể thử chỉ tính toán quan hệ lặp lại được xác định bên dưới?

http://en.wikipedia.org/wiki/Self_number

-1 hữu ích 1 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ c++ algorithm optimization , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading