প্রতিযোগিতা: গাউসিয়ান-বিতরণ করা ডেটার একটি বড় অ্যারে বাছাই করার দ্রুততম উপায়


71

এই প্রশ্নের আগ্রহের পরে , আমি ভেবেছিলাম প্রতিযোগিতার প্রস্তাব দিয়ে উত্তরগুলি আরও কিছু উদ্দেশ্যমূলক ও পরিমাণগত করা আকর্ষণীয় হবে।

ধারণাটি সহজ: আমি 50 মিলিয়ন গাউসী-বিতরণযুক্ত দ্বিগুণ (গড়: 0, স্টাডিভ 1) সহ একটি বাইনারি ফাইল তৈরি করেছি। লক্ষ্যটি এমন একটি প্রোগ্রাম তৈরি করা যা এগুলি মেমোরিতে যথাসম্ভব দ্রুত সাজিয়ে তোলে। পাইথনে খুব সাধারণ রেফারেন্স প্রয়োগকরণ সম্পূর্ণ হতে 1 মি 4 সেকেন্ড নেয়। আমরা কত নিচু যেতে পারি?

নিয়মগুলি নিম্নরূপ: একটি প্রোগ্রামের সাথে উত্তর দিন যা "গাউসিয়ান.ড্যাট" ফাইলটি খোলেন এবং মেমরিতে সংখ্যাগুলি সাজান (তাদের আউটপুট দেওয়ার দরকার নেই), এবং প্রোগ্রামটি তৈরি এবং পরিচালনা করার জন্য নির্দেশাবলী। প্রোগ্রামটি অবশ্যই আমার আর্চ লিনাক্স মেশিনে কাজ করতে সক্ষম হবে (এর অর্থ আপনি এই সিস্টেমে সহজে ইনস্টলযোগ্য কোনও প্রোগ্রামিং ভাষা বা লাইব্রেরি ব্যবহার করতে পারেন)।

প্রোগ্রামটি অবশ্যই যুক্তিসঙ্গতভাবে পঠনযোগ্য হতে হবে, যাতে আমি নিশ্চিত করতে পারি যে এটি চালু করা নিরাপদ কিনা (দয়া করে কেবল সমাবেশের জন্য কোনও সমাধান নেই, দয়া করে!)।

আমি আমার মেশিনে উত্তরগুলি চালাব (কোয়াড কোর, 4 গিগাবাইট র‌্যাম)। দ্রুততম সমাধানটি গৃহীত উত্তর এবং 100 পয়েন্টের অনুগ্রহ পাবে :)

সংখ্যাটি তৈরি করতে ব্যবহৃত প্রোগ্রাম:

#!/usr/bin/env python
import random
from array import array
from sys import argv
count=int(argv[1])
a=array('d',(random.gauss(0,1) for x in xrange(count)))
f=open("gaussian.dat","wb")
a.tofile(f)

সাধারণ রেফারেন্স বাস্তবায়ন:

#!/usr/bin/env python
from array import array
from sys import argv
count=int(argv[1])
a=array('d')
a.fromfile(open("gaussian.dat"),count)
print "sorting..."
b=sorted(a)

সম্পাদনা: র‌্যাম মাত্র 4 জিবি, দুঃখিত

সম্পাদনা # 2: নোট করুন যে প্রতিযোগিতার বিন্দুটি হ'ল আমরা ডেটা সম্পর্কে পূর্বের তথ্য ব্যবহার করতে পারি কিনা তা দেখতে হবে । এটি বিভিন্ন প্রোগ্রামিং ভাষার বাস্তবায়নের মধ্যে মাতাল ম্যাচ হওয়ার কথা নয়!


1
প্রতিটি মান নিন এবং এটিকে সরাসরি তার "প্রত্যাশিত" অবস্থানে নিয়ে যান, স্থানচ্যুত মানের জন্য পুনরাবৃত্তি করুন। কীভাবে এটি নিয়ে কোনও দম্পতি ইস্যু সমাধান করবেন তা নিশ্চিত নয়। শেষ হয়ে গেলে, বুদ্বুদ বাছাই সম্পূর্ণ না হওয়া পর্যন্ত (একটি দম্পতি পাস করা উচিত)।

1
আমি যদি কাল

1
@ স্ট্যাটিক_আরটি - ভারী সিজি ব্যবহারকারী হিসাবে এটি হ'ল সিজি.এসইতে "আমরা" হ্যাক করতে পছন্দ করি thing যে কোনও পঠন মোডে, এটি সিজি-তে সরান, এটি বন্ধ করবেন না।
অ্যারেডেম

1
কোডগলফ.এসই তে স্বাগতম! এটি কোথায় বা এর অন্তর্ভুক্ত নয় সে সম্পর্কিত এসও মূল থেকে আমি অনেকগুলি মন্তব্য সাফ করে দিয়েছি এবং কোডগলফ.এসই মূলধারার আরও কাছাকাছি থাকতে পুনরায় ট্যাগ করেছি।
ডিএমকেকে

2
এখানে একটি জটিল সমস্যা হ'ল আমরা উদ্দেশ্যমূলক জয়ের মানদণ্ডটি সন্ধান করি এবং "দ্রুততম" প্ল্যাটফর্ম নির্ভরতা প্রবর্তন করে ... সিপিথন ভার্চুয়াল মেশিনে প্রয়োগ করা কোনও ও (এন ^ {1.2}) অ্যালগরিদম কি একটি ওকে (n ^ {1.3 beat ) সি একই ধ্রুবক প্রয়োগ সঙ্গে অ্যালগরিদম? আমি প্রতিটি সমাধানের পারফরম্যান্স বৈশিষ্ট্যগুলি সম্পর্কে সাধারণত কিছু আলোচনার পরামর্শ দিই, কারণ এটি লোকেরা কী চলছে তা বিচার করতে সহায়তা করতে পারে।
ডিএমকেকে

উত্তর:


13

এখানে সি ++ তে একটি সমাধান রয়েছে যা প্রথমে একই প্রত্যাশিত সংখ্যক উপাদানগুলির সাথে বালিকেটে সংখ্যাগুলিকে ভাগ করে নেবে এবং তারপরে প্রতিটি বালতি পৃথকভাবে বাছাই করে। এটি উইকিপিডিয়া থেকে কিছু সূত্রের উপর ভিত্তি করে ক্রমবর্ধমান বিতরণ ফাংশনের একটি টেবিলকে পূর্বরূপ দেয় এবং তারপরে দ্রুত সান্নিধ্য পেতে এই টেবিল থেকে মানগুলি বিভক্ত করে।

চারটি করর ব্যবহার করতে একাধিক পদক্ষেপ একাধিক থ্রেডে চলে।

#include <cstdlib>
#include <math.h>
#include <stdio.h>
#include <algorithm>

#include <tbb/parallel_for.h>

using namespace std;

typedef unsigned long long ull;

double signum(double x) {
    return (x<0) ? -1 : (x>0) ? 1 : 0;
}

const double fourOverPI = 4 / M_PI;

double erf(double x) {
    double a = 0.147;
    double x2 = x*x;
    double ax2 = a*x2;
    double f1 = -x2 * (fourOverPI + ax2) / (1 + ax2);
    double s1 = sqrt(1 - exp(f1));
    return signum(x) * s1;
}

const double sqrt2 = sqrt(2);

double cdf(double x) {
    return 0.5 + erf(x / sqrt2) / 2;
}

const int cdfTableSize = 200;
const double cdfTableLimit = 5;
double* computeCdfTable(int size) {
    double* res = new double[size];
    for (int i = 0; i < size; ++i) {
        res[i] = cdf(cdfTableLimit * i / (size - 1));
    }
    return res;
}
const double* const cdfTable = computeCdfTable(cdfTableSize);

double cdfApprox(double x) {
    bool negative = (x < 0);
    if (negative) x = -x;
    if (x > cdfTableLimit) return negative ? cdf(-x) : cdf(x);
    double p = (cdfTableSize - 1) * x / cdfTableLimit;
    int below = (int) p;
    if (p == below) return negative ? -cdfTable[below] : cdfTable[below];
    int above = below + 1;
    double ret = cdfTable[below] +
            (cdfTable[above] - cdfTable[below])*(p - below);
    return negative ? 1 - ret : ret;
}

void print(const double* arr, int len) {
    for (int i = 0; i < len; ++i) {
        printf("%e; ", arr[i]);
    }
    puts("");
}

void print(const int* arr, int len) {
    for (int i = 0; i < len; ++i) {
        printf("%d; ", arr[i]);
    }
    puts("");
}

void fillBuckets(int N, int bucketCount,
        double* data, int* partitions,
        double* buckets, int* offsets) {
    for (int i = 0; i < N; ++i) {
        ++offsets[partitions[i]];
    }

    int offset = 0;
    for (int i = 0; i < bucketCount; ++i) {
        int t = offsets[i];
        offsets[i] = offset;
        offset += t;
    }
    offsets[bucketCount] = N;

    int next[bucketCount];
    memset(next, 0, sizeof(next));
    for (int i = 0; i < N; ++i) {
        int p = partitions[i];
        int j = offsets[p] + next[p];
        ++next[p];
        buckets[j] = data[i];
    }
}

class Sorter {
public:
    Sorter(double* data, int* offsets) {
        this->data = data;
        this->offsets = offsets;
    }

    static void radixSort(double* arr, int len) {
        ull* encoded = (ull*)arr;
        for (int i = 0; i < len; ++i) {
            ull n = encoded[i];
            if (n & signBit) {
                n ^= allBits;
            } else {
                n ^= signBit;
            }
            encoded[i] = n;
        }

        const int step = 11;
        const ull mask = (1ull << step) - 1;
        int offsets[8][1ull << step];
        memset(offsets, 0, sizeof(offsets));

        for (int i = 0; i < len; ++i) {
            for (int b = 0, j = 0; b < 64; b += step, ++j) {
                int p = (encoded[i] >> b) & mask;
                ++offsets[j][p];
            }
        }

        int sum[8] = {0};
        for (int i = 0; i <= mask; i++) {
            for (int b = 0, j = 0; b < 64; b += step, ++j) {
                int t = sum[j] + offsets[j][i];
                offsets[j][i] = sum[j];
                sum[j] = t;
            }
        }

        ull* copy = new ull[len];
        ull* current = encoded;
        for (int b = 0, j = 0; b < 64; b += step, ++j) {
            for (int i = 0; i < len; ++i) {
                int p = (current[i] >> b) & mask;
                copy[offsets[j][p]] = current[i];
                ++offsets[j][p];
            }

            ull* t = copy;
            copy = current;
            current = t;
        }

        if (current != encoded) {
            for (int i = 0; i < len; ++i) {
                encoded[i] = current[i];
            }
        }

        for (int i = 0; i < len; ++i) {
            ull n = encoded[i];
            if (n & signBit) {
                n ^= signBit;
            } else {
                n ^= allBits;
            }
            encoded[i] = n;
        }
    }

    void operator() (tbb::blocked_range<int>& range) const {
        for (int i = range.begin(); i < range.end(); ++i) {
            double* begin = &data[offsets[i]];
            double* end = &data[offsets[i+1]];
            //std::sort(begin, end);
            radixSort(begin, end-begin);
        }
    }

private:
    double* data;
    int* offsets;
    static const ull signBit = 1ull << 63;
    static const ull allBits = ~0ull;
};

void sortBuckets(int bucketCount, double* data, int* offsets) {
    Sorter sorter(data, offsets);
    tbb::blocked_range<int> range(0, bucketCount);
    tbb::parallel_for(range, sorter);
    //sorter(range);
}

class Partitioner {
public:
    Partitioner(int bucketCount, double* data, int* partitions) {
        this->data = data;
        this->partitions = partitions;
        this->bucketCount = bucketCount;
    }

    void operator() (tbb::blocked_range<int>& range) const {
        for (int i = range.begin(); i < range.end(); ++i) {
            double d = data[i];
            int p = (int) (cdfApprox(d) * bucketCount);
            partitions[i] = p;
        }
    }

private:
    double* data;
    int* partitions;
    int bucketCount;
};

const int bucketCount = 512;
int offsets[bucketCount + 1];

int main(int argc, char** argv) {
    if (argc != 2) {
        printf("Usage: %s N\n N = the size of the input\n", argv[0]);
        return 1;
    }

    puts("initializing...");
    int N = atoi(argv[1]);
    double* data = new double[N];
    double* buckets = new double[N];
    memset(offsets, 0, sizeof(offsets));
    int* partitions = new int[N];

    puts("loading data...");
    FILE* fp = fopen("gaussian.dat", "rb");
    if (fp == 0 || fread(data, sizeof(*data), N, fp) != N) {
        puts("Error reading data");
        return 1;
    }
    //print(data, N);

    puts("assigning partitions...");
    tbb::parallel_for(tbb::blocked_range<int>(0, N),
            Partitioner(bucketCount, data, partitions));

    puts("filling buckets...");
    fillBuckets(N, bucketCount, data, partitions, buckets, offsets);
    data = buckets;

    puts("sorting buckets...");
    sortBuckets(bucketCount, data, offsets);

    puts("done.");

    /*
    for (int i = 0; i < N-1; ++i) {
        if (data[i] > data[i+1]) {
            printf("error at %d: %e > %e\n", i, data[i], data[i+1]);
        }
    }
    */

    //print(data, N);

    return 0;
}

এটি সঙ্কলন এবং পরিচালনা করতে, এই আদেশটি ব্যবহার করুন:

g++ -O3 -ltbb -o gsort gsort.cpp && time ./gsort 50000000

সম্পাদনা: বালতিগুলি আবার অ্যারেতে অনুলিপি করার প্রয়োজনটি সরাতে এখন সমস্ত বালতি একই অ্যারেতে স্থাপন করা হয়। এছাড়াও প্রাক্পম্পিউটেড মান সহ টেবিলের আকার হ্রাস করা হয়েছিল, কারণ মানগুলি যথেষ্ট সঠিক are তবুও, যদি আমি বালতিগুলির সংখ্যা 256 এর উপরে পরিবর্তন করি তবে প্রোগ্রামটি বালতি সংখ্যার চেয়ে বেশি সময় নেয়।

সম্পাদনা: একই অ্যালগরিদম, বিভিন্ন প্রোগ্রামিং ভাষা। আমি জাভার পরিবর্তে সি ++ ব্যবহার করেছি এবং চলমান সময়টি আমার মেশিনে 2 3.2 থেকে কমিয়ে ~ 2.35s এ চলেছে। বালতিগুলির সর্বোত্তম সংখ্যা এখনও 256 (আবার আমার কম্পিউটারে) এর কাছাকাছি।

যাইহোক, টিবিবি সত্যিই দুর্দান্ত।

সম্পাদনা: আমি আলেকজানড্রুর দুর্দান্ত সমাধান দ্বারা অনুপ্রাণিত হয়ে শেষ পর্যায়ে তার র‌্যাডিক্সের পরিবর্তিত সংস্করণ দ্বারা স্ট্যান্ড :: সাজকে প্রতিস্থাপন করেছি। ইতিবাচক / নেতিবাচক সংখ্যাগুলি মোকাবেলায় আমি একটি আলাদা পদ্ধতি ব্যবহার করেছি, যদিও অ্যারে দিয়ে আরও পাসের প্রয়োজন হয়। আমি ঠিক করেছিলাম অ্যারেটি ঠিক ঠিক সাজানো এবং সন্নিবেশ সাজানোর ব্যবস্থা সরিয়ে ফেলুন। আমি পরে কিছু সময় ব্যয় করব যা কীভাবে এই পরিবর্তনগুলি কার্য সম্পাদনকে প্রভাবিত করে এবং সম্ভবত এগুলিকে ফিরিয়ে দেয়। তবে, র‌্যাডিক্স সাজ্ট ব্যবহার করে সময়টি ~ 2.35s থেকে হ্রাস পেয়ে 63 1.63s এ এসেছিল।


খুশী হলাম। আমি আমার উপর 3.055 পেয়েছি। সর্বনিম্ন আমি আমার পেতে সক্ষম হয়েছিল 6.3। আমি পরিসংখ্যান আরও ভাল পেতে আপনার মাধ্যমে বাছাই করছি। কেন আপনি বালতি সংখ্যা হিসাবে 256 বেছে নিয়েছেন? আমি 128 এবং 512 চেষ্টা করেছি, এখনও 256 সবচেয়ে ভাল কাজ করেছে।
স্কট

আমি কেন বালতি সংখ্যা হিসাবে 256 বেছে নিয়েছি? আমি 128 এবং 512 চেষ্টা করেছি, এখনও 256 সবচেয়ে ভাল কাজ করেছে। :) আমি এটি অনুপ্রাণিতভাবে পেয়েছি এবং আমি নিশ্চিত নই কেন বালতির সংখ্যা বাড়ানো অ্যালগরিদমকে কমিয়ে দেয় - মেমরির বরাদ্দটি এত বেশি সময় নেয় না। ক্যাশে আকার সম্পর্কিত কিছু হতে পারে?
21

আমার মেশিনে 2.725s। জেভিএমের লোডিংয়ের সময়টি বিবেচনায় নিয়ে জাভা সমাধানের জন্য খুব সুন্দর।
স্থির_আরত্তি

2
আমার এবং আরজানের সমাধান অনুযায়ী নিও প্যাকেজগুলি ব্যবহার করার জন্য আমি আপনার কোডটি স্যুইচ করেছি (তার সিনট্যাক্সটি ব্যবহার করা হয়েছে, যেহেতু এটি আমার চেয়ে পরিষ্কার ছিল) এবং এটি .3 সেকেন্ড দ্রুত পেতে সক্ষম হয়েছিল। আমি একটি এসএসডি পেয়েছি, আমি অবাক হয়েছি যদি না হয় তবে এর কী কী প্রভাব থাকতে পারে। এটি আপনার কিছু বিট টুইডলিং থেকে মুক্তি পায়। মোডেড বিভাগগুলি এখানে রয়েছে।
স্কট

3
এটি আমার পরীক্ষাগুলির দ্রুততম সমান্তরাল সমাধান (16कोर সিপিইউ)। 1.22 দূরে 1.94s দ্বিতীয় স্থান থেকে।
আলেকজান্দ্রু

13

স্মার্ট না হয়ে, কেবল আরও দ্রুত সর্বাগ্রে সরবরাহকারী সরবরাহের জন্য, এখানে সি তে একটি যা আপনার পাইথনের তুলনায় বেশ সমান হতে হবে:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmp(const void* av, const void* bv) {
    double a = *(const double*)av;
    double b = *(const double*)bv;
    return a < b ? -1 : a > b ? 1 : 0;
}
int main(int argc, char** argv) {
    if (argc <= 1)
        return puts("No argument!");
    unsigned count = atoi(argv[1]);

    double *a = malloc(count * sizeof *a);

    FILE *f = fopen("gaussian.dat", "rb");
    if (fread(a, sizeof *a, count, f) != count)
        return puts("fread failed!");
    fclose(f);

    puts("sorting...");
    double *b = malloc(count * sizeof *b);
    memcpy(b, a, count * sizeof *b);
    qsort(b, count, sizeof *b, cmp);
    return 0;
}

সংকলিত gcc -O3, আমার মেশিনে এটি পাইথনের চেয়ে এক মিনিটেরও বেশি সময় নেয়: 87 টির তুলনায় প্রায় 11 টি।


1
আমার মেশিনে 10.086 গুলি নিয়েছে, যা আপনাকে বর্তমান নেতা করে তোলে! তবে আমি নিশ্চিত যে আমরা আরও ভাল করতে পারি :)

1
আপনি দ্বিতীয় তৃতীয় অপারেটর অপসারণ করার চেষ্টা করতে পারেন এবং কেবল সেই ক্ষেত্রে 1 ফিরে আসেন কারণ এলোমেলো ডাবলগুলি এই পরিমাণ ডেটাতে একে অপরের সমান নয়।
কোডিজম

@ কোডিজম: আমি যুক্ত করব যে আমরা সমতুল্য ডেটার অবস্থানগুলি অদলবদলের বিষয়ে চিন্তা করি না তাই আমরা সমমানের মানগুলি পেতে পারলেও এটি একটি উপযুক্ত সরলীকরণ হবে।

10

আমি স্ট্যান্ডার্ড বিচ্যুতির উপর ভিত্তি করে বিভাগগুলিতে বিভক্ত হয়েছি যে এটি এটিকে সর্বোপরি 4 র্থ ভাগে ভাগ করা উচিত। সম্পাদনা করুন: http://en.wikedia.org/wiki/Error_function# টেবিল_ের_মূল্যগুলিতে x মানের ভিত্তিতে পার্টিশনে পুনরায় লিখিত

http://www.wolframalpha.com/input/?i=percentages+by++normal+distribution

আমি আরও ছোট বালতি ব্যবহার করার চেষ্টা করেছি, তবে মনে হয় এটি উপলব্ধ করের সংখ্যা ছাড়িয়ে একবার 2 * একবারে খুব কম প্রভাব ফেলবে। কোনও সমান্তরাল সংগ্রহ ছাড়াই, এটি আমার বাক্সে 37 সেকেন্ড এবং 24 টি সমান্তরাল সংগ্রহের সাথে লাগবে। যদি বিতরণের মাধ্যমে বিভাজন হয়, আপনি কেবল একটি অ্যারে ব্যবহার করতে পারবেন না, তাই আরও কিছু ওভারহেড রয়েছে। স্ক্যালায় কখন কোনও মান বক্স / আনবক্স করা হবে সে বিষয়ে আমি পরিষ্কার নই clear

আমি সমান্তরাল সংগ্রহের জন্য স্কেল ২.৯ ব্যবহার করছি। আপনি কেবল এটির tar.gz বিতরণ ডাউনলোড করতে পারেন।

সংকলন করতে: স্কেল্যাক SortFile.scala (আমি কেবল এটি সরাসরি স্কাল / বিন ফোল্ডারে অনুলিপি করেছি।

চালানোর জন্য: JAVA_OPTS = "- Xmx4096M"। / স্কালা SortFile (আমি এটি 2 জিগ র্যাম নিয়ে চালিয়েছি এবং প্রায় একই সময়ে পেয়েছি)

সম্পাদনা: সরানো বরাদ্দ প্রত্যক্ষ, শুধু বরাদ্দ চেয়ে ধীর। অ্যারে বাফারগুলির জন্য প্রাথমিক আকারের প্রাইমিং সরানো হয়েছে। আসলে এটি পুরো 50000000 মান পড়তে বাধ্য করে made আশা করি অটোবক্সিং সম্পর্কিত সমস্যাগুলি এড়িয়ে চলার জন্য পুনরায় লিখেছিলেন (নিষ্পাপ গের চেয়ে এখনও ধীর)

import java.io.FileInputStream;
import java.nio.ByteBuffer
import java.nio.ByteOrder
import scala.collection.mutable.ArrayBuilder


object SortFile {

//used partition numbers from Damascus' solution
val partList = List(0, 0.15731, 0.31864, 0.48878, 0.67449, 0.88715, 1.1503, 1.5341)

val listSize = partList.size * 2;
val posZero = partList.size;
val neg = partList.map( _ * -1).reverse.zipWithIndex
val pos = partList.map( _ * 1).zipWithIndex.reverse

def partition(dbl:Double): Int = { 

//for each partition, i am running through the vals in order
//could make this a binary search to be more performant... but our list size is 4 (per side)

  if(dbl < 0) { return neg.find( dbl < _._1).get._2  }
  if(dbl > 0) { return posZero  + pos.find( dbl > _._1).get._2  }
      return posZero; 

}

  def main(args: Array[String])
    { 

    var l = 0
    val dbls = new Array[Double](50000000)
    val partList = new Array[Int](50000000)
    val pa = Array.fill(listSize){Array.newBuilder[Double]}
    val channel = new FileInputStream("../../gaussian.dat").getChannel()
    val bb = ByteBuffer.allocate(50000000 * 8)
    bb.order(ByteOrder.LITTLE_ENDIAN)
    channel.read(bb)
    bb.rewind
    println("Loaded" + System.currentTimeMillis())
    var dbl = 0.0
    while(bb.hasRemaining)
    { 
      dbl = bb.getDouble
      dbls.update(l,dbl) 

      l+=1
    }
    println("Beyond first load" + System.currentTimeMillis());

    for( i <- (0 to 49999999).par) { partList.update(i, partition(dbls(i)))}

    println("Partition computed" + System.currentTimeMillis() )
    for(i <- (0 to 49999999)) { pa(partList(i)) += dbls(i) }
    println("Partition completed " + System.currentTimeMillis())
    val toSort = for( i <- pa) yield i.result()
    println("Arrays Built" + System.currentTimeMillis());
    toSort.par.foreach{i:Array[Double] =>scala.util.Sorting.quickSort(i)};

    println("Read\t" + System.currentTimeMillis());

  }
}

1
8.185s! একটি স্কেলার সমাধানের জন্য দুর্দান্ত, আমি অনুমান করি ... এছাড়াও, প্রথম সমাধানটি সরবরাহ করার জন্য ব্র্যাভো যা আসলে কোনওভাবে গাউসীয় বিতরণ ব্যবহার করে!

1
আমি কেবল সি # সমাধানের সাথে প্রতিযোগিতা করার লক্ষ্য নিয়েছিলাম। আমি সি / সি ++ মারতাম তা বুঝতে পারিনি। এছাড়াও .. এটি আমার চেয়ে আপনার চেয়ে অনেক আলাদা আচরণ করছে। আমি আমার শেষ প্রান্তে ওপেনজেডিকে ব্যবহার করছি এবং এটি অনেক ধীর। আমি ভাবছি যদি আরও পার্টিশন যুক্ত করা আপনার vভুতে সহায়তা করে।
স্কট

9

এটি কেবল একটি সিএস ফাইলে রাখুন এবং এটি সিএসসি দিয়ে তত্ত্বের সাথে সংকলন করুন: (মনো প্রয়োজন)

using System;
using System.IO;
using System.Threading;

namespace Sort
{
    class Program
    {
        const int count = 50000000;
        static double[][] doubles;
        static WaitHandle[] waiting = new WaitHandle[4];
        static AutoResetEvent[] events = new AutoResetEvent[4];

        static double[] Merge(double[] left, double[] right)
        {
            double[] result = new double[left.Length + right.Length];
            int l = 0, r = 0, spot = 0;
            while (l < left.Length && r < right.Length)
            {
                if (right[r] < left[l])
                    result[spot++] = right[r++];
                else
                    result[spot++] = left[l++];
            }
            while (l < left.Length) result[spot++] = left[l++];
            while (r < right.Length) result[spot++] = right[r++];
            return result;
        }

        static void ThreadStart(object data)
        {
            int index = (int)data;
            Array.Sort(doubles[index]);
            events[index].Set();
        }

        static void Main(string[] args)
        {
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();
            byte[] bytes = File.ReadAllBytes(@"..\..\..\SortGuassian\Data.dat");
            doubles = new double[][] { new double[count / 4], new double[count / 4], new double[count / 4], new double[count / 4] };
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < count / 4; j++)
                {
                    doubles[i][j] = BitConverter.ToDouble(bytes, i * count/4 + j * 8);
                }
            }
            Thread[] threads = new Thread[4];
            for (int i = 0; i < 4; i++)
            {
                threads[i] = new Thread(ThreadStart);
                waiting[i] = events[i] = new AutoResetEvent(false);
                threads[i].Start(i);
            }
            WaitHandle.WaitAll(waiting);
            double[] left = Merge(doubles[0], doubles[1]);
            double[] right = Merge(doubles[2], doubles[3]);
            double[] result = Merge(left, right);
            watch.Stop();
            Console.WriteLine(watch.Elapsed.ToString());
            Console.ReadKey();
        }
    }
}

আমি কি মনোয়ার সাথে আপনার সমাধানগুলি চালাতে পারি? আমি এটা কিভাবে করব?

মনো ব্যবহার করেন নি, এটি ভাবেননি, আপনার F # সংকলন করতে হবে এবং তারপরে এটি চালানো উচিত।

1
কর্মক্ষমতা উন্নত করতে চারটি থ্রেড ব্যবহার করার জন্য আপডেট হয়েছে। এখন আমাকে 6 সেকেন্ড দেয়। নোট করুন যে এটি মাত্র একটি স্প্রে অ্যারে ব্যবহার করে এবং এক টন মেমরি শূন্যে শুরু করা এড়ানোর জন্য এটিকে উল্লেখযোগ্যভাবে উন্নতি করতে পারে (5 সেকেন্ড সম্ভবত), সবকিছু সিএলআর দ্বারা সম্পন্ন হয়েছে, যেহেতু কমপক্ষে একবারে সমস্ত কিছু লেখা হয়ে থাকে।

1
আমার মেশিনে 9.598s! আপনি বর্তমান নেতা :)

1
আমার মা আমাকে বলেছিলেন মনোর সাথে ছেলেদের থেকে দূরে থাকুন!

8

যেহেতু আপনি জানেন যে বিতরণটি কী, আপনি সরাসরি ইনডেক্সিং ও (এন) বাছাই করতে পারেন। (যদি আপনি এটি ভাবছেন তবে, ধরুন আপনার কাছে 52 টি কার্ডের একটি ডেক রয়েছে এবং আপনি এটি বাছাই করতে চান 52 52 টি বিন আছে এবং প্রতিটি কার্ডের নিজস্ব বাক্সে টস করুন))

আপনার 5e7 ডাবলস রয়েছে। 5e7 ডাবলসের ফলাফল অ্যারে আর বরাদ্দ করুন। প্রতিটি নম্বর নিন xএবং পাবেন i = phi(x) * 5e7। মূলত কি R[i] = x। সংঘর্ষগুলি হ্যান্ডেল করার একটি উপায় রাখুন, যেমন এটি সংঘটিত হতে পারে এমন নম্বরটি সরানো (সাধারণ হ্যাশ কোডিংয়ের মতো)। বিকল্পভাবে, আপনি আর কয়েক গুণ বড় করতে পারেন, একটি অনন্য শূন্য মান দিয়ে ভরা । শেষে, আপনি কেবল আর এর উপাদানগুলি স্যুইপ করেন।

phiকেবল গাউসিয়ান ক্রম বিতরণ ফাংশন। এটি গাউসীয় বিতরণ সংখ্যাটিকে +/- অনন্তর মধ্যে 0 থেকে 1 এর মধ্যে অভিন্ন বিতরণ সংখ্যায় রূপান্তর করে it


3
সাবধানতা: আপনি আনুমানিক বিতরণ জানেন, সঠিক বিতরণ নয়। আপনি জানেন যে একটি গাউসিয়ান আইন ব্যবহার করে ডেটা তৈরি করা হয়েছিল তবে এটি সীমাবদ্ধ হওয়ায় এটি কোনও গাউসিয়ানকে অনুসরণ করে না।

@ স্ট্যাটিক_আরটি: এক্ষেত্রে ফাইয়ের প্রয়োজনীয় আনুমানিকতা তথ্য সেট আইএমও-তে যে কোনও অনিয়মের চেয়ে বড় ঝামেলা তৈরি করবে।

1
@ স্ট্যাটিক_আরটি: এটি সঠিক হতে হবে না। এটি কেবলমাত্র ডেটা ছড়িয়ে দিতে হবে সুতরাং এটি প্রায় অভিন্ন, তাই এটি কিছু জায়গায় খুব বেশি গুচ্ছ না।

মনে করুন আপনার 5e7 ডাবলস রয়েছে। কেন আর আর প্রতিটি প্রবেশকে ভেক্টরের ভ্যাক্টর করে না, বলুন, 5e6 ভ্যাক্টরের দ্বিগুণ। তারপরে, যথাযথ ভেক্টরে প্রতিটি ডাবল পুশ_ব্যাক করুন। ভেক্টরগুলিকে বাছাই করুন এবং আপনার কাজ শেষ হয়েছে। এটি ইনপুট আকারে রৈখিক সময় নিতে হবে।
নিল জি

আসলে, আমি দেখতে পাচ্ছি যে এমডিকেস ইতিমধ্যে সেই সমাধানটি নিয়ে এসেছে।
নিল জি

8

এখানে আরও একটি ক্রমিক সমাধান রয়েছে:

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

typedef unsigned long long ull;

int size;
double *dbuf, *copy;
int cnt[8][1 << 16];

void sort()
{
  const int step = 10;
  const int start = 24;
  ull mask = (1ULL << step) - 1;

  ull *ibuf = (ull *) dbuf;
  for (int i = 0; i < size; i++) {
    for (int w = start, v = 0; w < 64; w += step, v++) {
      int p = (~ibuf[i] >> w) & mask;
      cnt[v][p]++;
    }
  }

  int sum[8] = { 0 };
  for (int i = 0; i <= mask; i++) {
    for (int w = start, v = 0; w < 64; w += step, v++) {
      int tmp = sum[v] + cnt[v][i];
      cnt[v][i] = sum[v];
      sum[v] = tmp;
    }
  }

  for (int w = start, v = 0; w < 64; w += step, v++) {
    ull *ibuf = (ull *) dbuf;
    for (int i = 0; i < size; i++) {
      int p = (~ibuf[i] >> w) & mask;
      copy[cnt[v][p]++] = dbuf[i];
    }

    double *tmp = copy;
    copy = dbuf;
    dbuf = tmp;
  }

  for (int p = 0; p < size; p++)
    if (dbuf[p] >= 0.) {
      std::reverse(dbuf + p, dbuf + size);
      break;
    }

  // Insertion sort
  for (int i = 1; i < size; i++) {
    double value = dbuf[i];
    if (value < dbuf[i - 1]) {
      dbuf[i] = dbuf[i - 1];
      int p = i - 1;
      for (; p > 0 && value < dbuf[p - 1]; p--)
        dbuf[p] = dbuf[p - 1];
      dbuf[p] = value;
    }
  }
}

int main(int argc, char **argv) {
  size = atoi(argv[1]);
  dbuf = new double[size];
  copy = new double[size];

  FILE *f = fopen("gaussian.dat", "r");
  fread(dbuf, size, sizeof(double), f);
  fclose(f);

  clock_t c0 = clock();
  sort();
  printf("Finished after %.3f\n", (double) ((clock() - c0)) / CLOCKS_PER_SEC);
  return 0;
}

আমি সন্দেহ করি এটি মাল্টি-থ্রেডযুক্ত সমাধানটিকে মারধর করে তবে আমার আই 7 ল্যাপটপের সময়গুলি হ'ল (স্ট্যান্ডসোর্টটি হ'ল সি ++ সলিউশন অন্য উত্তরে প্রদত্ত):

$ g++ -O3 mysort.cpp -o mysort && ./mysort 50000000
Finished after 2.10
$ g++ -O3 stdsort.cpp -o stdsort && ./stdsort
Finished after 7.12

নোট করুন যে এই সমাধানটির লিনিয়ার সময় জটিলতা রয়েছে (কারণ এটি ডাবলসের বিশেষ উপস্থাপনা ব্যবহার করে)।

সম্পাদনা : উপাদানগুলির ক্রম বাড়ানোর জন্য স্থির।

সম্পাদনা : প্রায় অর্ধেক সেকেন্ডে গতি উন্নত

সম্পাদনা : অন্য 0.7 সেকেন্ড দ্বারা গতি উন্নত অ্যালগরিদম আরও ক্যাশে বন্ধুত্বপূর্ণ তৈরি।

সম্পাদনা : আরও 1 সেকেন্ড দ্বারা গতি উন্নত যেহেতু কেবলমাত্র 50.000.000 উপাদান রয়েছে আমি আংশিকভাবে ম্যান্টিসাকে বাছাই করতে পারি এবং স্থানের বাইরে থাকা উপাদানগুলি ঠিক করার জন্য সন্নিবেশ সাজান (যা ক্যাশে বান্ধব) ব্যবহার করতে পারি। এই ধারণাটি শেষ রেডিক্সের বাছাই করা লুপ থেকে প্রায় দুটি পুনরাবৃত্তি সরিয়ে দেয়।

সম্পাদনা : 0.16 কম সেকেন্ড। বাছাইয়ের ক্রমটি বিপরীত হলে প্রথম স্ট্যান্ড :: বিপরীত মুছে ফেলা যায়।


এখন এটি আকর্ষণীয় হয়ে উঠছে! এটি কোন ধরণের অ্যালগরিদম?
স্থির_আরত্তি

2
স্বল্পতম অঙ্কের রেডিক্স সাজান । আপনি ম্যান্টিসা বাছাই করতে পারেন, তারপরে সূচকটি, তারপর সাইন এখানে উপস্থাপন করা অ্যালগরিদম এই ধারণাটিকে আরও একধাপ এগিয়ে নিয়ে যায়। এটি একটি পৃথক উত্তরে প্রদত্ত বিভাজন ধারণা ব্যবহার করে সমান্তরাল করা যেতে পারে।
আলেকজান্দ্রু

একক থ্রেডযুক্ত সমাধানের জন্য খুব দ্রুত: 2.552 এস! আপনি কি মনে করেন যে তথ্যটি সাধারণত বিতরণ করা হয় তা ব্যবহার করার জন্য আপনি নিজের সমাধানটি পরিবর্তন করতে পারেন? আপনি সম্ভবত বর্তমান সেরা মাল্টি-থ্রেড সমাধানগুলির চেয়ে আরও ভাল করতে পারেন।
স্থির_আরত্তি

1
@ স্ট্যাটিক_আরটি: আমি দেখতে পাচ্ছি যে দামাস্কাস স্টিল ইতিমধ্যে এই প্রয়োগের একটি বহুবিধ সংস্করণ পোস্ট করেছে। আমি এই অ্যালগরিদমের ক্যাশিং আচরণটি উন্নত করেছি, সুতরাং আপনার এখন আরও ভাল সময় পাওয়া উচিত। দয়া করে এই নতুন সংস্করণটি পরীক্ষা করুন।
আলেকজান্দ্রু

2
আমার ল্যাটস পরীক্ষাগুলিতে 1.459s। যদিও এই বিধিটি আমার বিধি অনুসারে বিজয়ী নয়, এটি সত্যই বড় কুডোর প্রাপ্য। অভিনন্দন!
স্থির_আরত্তি

6

ক্রিশ্চিয়ান আম্মারের সমাধান গ্রহণ এবং এটি ইন্টেলের থ্রেডেড বিল্ডিং ব্লকগুলির সাথে সমান্তরাল করে তুলছে

#include <iostream>
#include <fstream>
#include <algorithm>
#include <vector>
#include <ctime>
#include <tbb/parallel_sort.h>

int main(void)
{
    std::ifstream ifs("gaussian.dat", std::ios::binary | std::ios::in);
    std::vector<double> values;
    values.reserve(50000000);
    double d;
    while (ifs.read(reinterpret_cast<char*>(&d), sizeof(double)))
    values.push_back(d);
    clock_t c0 = clock();
    tbb::parallel_sort(values.begin(), values.end());
    std::cout << "Finished after "
              << static_cast<double>((clock() - c0)) / CLOCKS_PER_SEC
              << std::endl;
}

আপনার যদি ইন্টেলের পারফরম্যান্স প্রিমিটিভস (আইপিপি) লাইব্রেরিতে অ্যাক্সেস থাকে তবে আপনি এর রেডিক্স বাছাই করতে পারেন। শুধু প্রতিস্থাপন

#include <tbb/parallel_sort.h>

সঙ্গে

#include "ipps.h"

এবং

tbb::parallel_sort(values.begin(), values.end());

সঙ্গে

std::vector<double> copy(values.size());
ippsSortRadixAscend_64f_I(&values[0], &copy[0], values.size());

আমার ডুয়াল কোর ল্যাপটপে, সময়গুলি

C               16.4 s
C#              20 s
C++ std::sort   7.2 s
C++ tbb         5 s
C++ ipp         4.5 s
python          too long

1
2.958s! টিবিবি দেখতে বেশ দুর্দান্ত এবং সহজেই ব্যবহারযোগ্য!

2
টিবিবি অযৌক্তিকভাবে দুর্দান্ত। এটি ঠিক অ্যালগোরিদমিক কাজের জন্য বিমূর্তির সঠিক স্তরের।
drxzcl

5

সমান্তরাল কুইকোর্টের একটি বাস্তবায়ন সম্পর্কে কীভাবে সমান আকারের পার্টিশন নিশ্চিত করে বিতরণের পরিসংখ্যানের ভিত্তিতে এর পিভট মানগুলি চয়ন করে? প্রথম পাইভটটি মাঝামাঝি হবে (এক্ষেত্রে শূন্য), পরের জোড়টি 25 তম এবং 75 তম পার্সেন্টাইল (+/- -0.67449 স্ট্যান্ডার্ড বিচ্যুতি) এ থাকবে এবং এইভাবে, প্রতিটি পার্টিশন বাকী ডেটা আরও অর্ধেক স্থির করে রাখবে বা কম নিখুঁত।


এটি কার্যকরভাবে আমি আমার উপর যা করেছি .. অবশ্যই আমার লেখা শেষ করার আগে আপনি এই পোস্টটি পেয়েছেন।

5

খুব কুরুচিপূর্ণ (যখন আমি সংখ্যার সাথে সমাপ্তি ভেরিয়েবলগুলি ব্যবহার করতে পারি তখন অ্যারেগুলি কেন ব্যবহার করি), তবে দ্রুত কোড (আমার প্রথম স্ট্যান্ডার্ড :: থ্রেডের চেষ্টা), আমার সিস্টেমে পুরো সময় (সময় বাস্তব) 1,8 গুলি (স্ট্যান্ডার্ড :: সাজানোর সাথে তুলনা করে) () 4,8 s), g ++ -std = c ++ 0x -O3 -march = নেটিভ-প্রসারিত স্ট্যান্ডিনের মাধ্যমে কেবল ডেটা পাস করুন (কেবল 50M এর জন্য কাজ করে) comp

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <thread>
using namespace std;
const size_t size=50000000;

void pivot(double* start,double * end, double middle,size_t& koniec){
    double * beg=start;
    end--;
    while (start!=end){
        if (*start>middle) swap (*start,*end--);
        else start++;
    }
    if (*end<middle) start+=1;
    koniec= start-beg;
}
void s(double * a, double* b){
    sort(a,b);
}
int main(){
    double *data=new double[size];
    FILE *f = fopen("gaussian.dat", "rb");
    fread(data,8,size,f);
    size_t end1,end2,end3,temp;
    pivot(data, data+size,0,end2);
    pivot(data, data+end2,-0.6745,end1);
    pivot(data+end2,data+size,0.6745,end3);
    end3+=end2;
    thread ts1(s,data,data+end1);
    thread ts2(s,data+end1,data+end2);
    thread ts3(s,data+end2,data+end3);
    thread ts4(s,data+end3,data+size);
    ts1.join(),ts2.join(),ts3.join(),ts4.join();
    //for (int i=0; i<size-1; i++){
    //  if (data[i]>data[i+1]) cerr<<"BLAD\n";
    //}
    fclose(f);
    //fwrite(data,8,size,stdout);
}

// সম্পাদনাটি গসিয়ান.ড্যাট ফাইলটি পড়তে পরিবর্তিত হয়েছে।


উপরের সি ++ সমাধানগুলি যেমন আপনি গাউস.ড্যাট পড়তে এটি পরিবর্তন করতে পারেন?

আমি বাসায় এলে পরে চেষ্টা করব।
স্থির_আরত্তি

খুব সুন্দর সমাধান, আপনি বর্তমান নেতা (1.949s)! এবং
গাউসীয় বিতরণটির দুর্দান্ত

4

একটি সি ++ সমাধান ব্যবহার করে std::sort(কিউসোর্টের তুলনায় পরিশেষে দ্রুত , স্টেড :: সাজ্ট )

#include <iostream>
#include <fstream>
#include <algorithm>
#include <vector>
#include <ctime>

int main(void)
{
    std::ifstream ifs("C:\\Temp\\gaussian.dat", std::ios::binary | std::ios::in);
    std::vector<double> values;
    values.reserve(50000000);
    double d;
    while (ifs.read(reinterpret_cast<char*>(&d), sizeof(double)))
        values.push_back(d);
    clock_t c0 = clock();
    std::sort(values.begin(), values.end());
    std::cout << "Finished after "
              << static_cast<double>((clock() - c0)) / CLOCKS_PER_SEC
              << std::endl;
}

আমি নির্ভরযোগ্যভাবে বলতে পারি না যে এটি কতক্ষণ সময় নেয় কারণ আমার মেশিনে আমার কেবল 1 জিবি রয়েছে এবং প্রদত্ত পাইথন কোডটি দিয়ে আমি gaussian.datকেবলমাত্র 25 মিমি ডাবল (কোনও স্মৃতি ত্রুটি না পেয়ে) দিয়ে একটি ফাইল তৈরি করতে পারি । তবে আমি খুব আগ্রহী যে স্ট্যান্ড :: সাজানোর অ্যালগোরিদম কত দিন চলে।


6.425s! প্রত্যাশিত হিসাবে, সি ++ জ্বলছে :)

@ স্ট্যাটিক_আরটি: আমি টিমসোর্ট অ্যালগরিদমের স্বনসন চেষ্টা করেছি (যেমন আপনার প্রথম প্রশ্নে ম্যাথিউ এম। এর পরামর্শ অনুসারে )। sort.hসি ++ দিয়ে সংকলন করতে আমাকে ফাইলটিতে কিছু পরিবর্তন করতে হয়েছিল । এটি তুলনায় প্রায় দ্বিগুণ ছিল std::sort। কেন জানি না, সম্ভবত সংকলক অপ্টিমাইজেশনের কারণে?
খ্রিস্টান আম্মার

4

এখানে জাজেরেকের থ্রেডেড স্মার্ট পাইভটিংয়ের সাথে আলেকজান্দ্রির র‌ডিক্স সাজানোর মিশ্রণ রয়েছে। এটি দিয়ে সংকলন

g++ -std=c++0x -pthread -O3 -march=native sorter_gaussian_radix.cxx -o sorter_gaussian_radix

আপনি STEP (উদাহরণস্বরূপ -DSTEP = 11) সংজ্ঞায়িত করে র‌ডিক্সের আকার পরিবর্তন করতে পারেন। আমি আমার ল্যাপটপের সেরা 8 টি পেয়েছি (ডিফল্ট)।

ডিফল্টরূপে, এটি সমস্যাটি 4 টুকরো টুকরো টুকরো করে এবং এটি একাধিক থ্রেডে চালিত হয়। কমান্ড লাইনে একটি গভীরতার পরামিতি পেরিয়ে আপনি এটি পরিবর্তন করতে পারেন। সুতরাং আপনার যদি দুটি কোর থাকে তবে এটি চালান

sorter_gaussian_radix 50000000 1

এবং যদি আপনার কাছে 16 টি কোর থাকে

sorter_gaussian_radix 50000000 4

এই মুহূর্তে সর্বাধিক গভীরতা 6 (64 থ্রেড)। আপনি যদি অনেকগুলি স্তর রাখেন তবে আপনি কোডটি কমিয়ে দেবেন।

একটি জিনিস আমি চেষ্টা করেছিলাম সেটি হ'ল ইনটেল পারফরম্যান্স প্রিমিটিভস (আইপিপি) লাইব্রেরি থেকে রেডিক্স সাজান। আলেকজান্দ্রুর প্রয়োগ বাস্তবায়িতভাবে আইপিপিকে কষ্ট দেয়, আইপিপি প্রায় 30% ধীর। এই প্রকরণটি এখানেও অন্তর্ভুক্ত করা হয়েছে (মন্তব্য করা হয়েছে)।

#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <ctime>
#include <iostream>
#include <thread>
#include <vector>
#include <boost/cstdint.hpp>
// #include "ipps.h"

#ifndef STEP
#define STEP 8
#endif

const int step = STEP;
const int start_step=24;
const int num_steps=(64-start_step+step-1)/step;
int size;
double *dbuf, *copy;

clock_t c1, c2, c3, c4, c5;

const double distrib[]={-2.15387,
                        -1.86273,
                        -1.67594,
                        -1.53412,
                        -1.4178,
                        -1.31801,
                        -1.22986,
                        -1.15035,
                        -1.07752,
                        -1.00999,
                        -0.946782,
                        -0.887147,
                        -0.830511,
                        -0.776422,
                        -0.724514,
                        -0.67449,
                        -0.626099,
                        -0.579132,
                        -0.53341,
                        -0.488776,
                        -0.445096,
                        -0.40225,
                        -0.36013,
                        -0.318639,
                        -0.27769,
                        -0.237202,
                        -0.197099,
                        -0.157311,
                        -0.11777,
                        -0.0784124,
                        -0.0391761,
                        0,
                        0.0391761,
                        0.0784124,
                        0.11777,
                        0.157311,
                        0.197099,
                        0.237202,
                        0.27769,
                        0.318639,
                        0.36013,
                        0.40225,
                        0.445097,
                        0.488776,
                        0.53341,
                        0.579132,
                        0.626099,
                        0.67449,
                        0.724514,
                        0.776422,
                        0.830511,
                        0.887147,
                        0.946782,
                        1.00999,
                        1.07752,
                        1.15035,
                        1.22986,
                        1.31801,
                        1.4178,
                        1.53412,
                        1.67594,
                        1.86273,
                        2.15387};


class Distrib
{
  const int value;
public:
  Distrib(const double &v): value(v) {}

  bool operator()(double a)
  {
    return a<value;
  }
};


void recursive_sort(const int start, const int end,
                    const int index, const int offset,
                    const int depth, const int max_depth)
{
  if(depth<max_depth)
    {
      Distrib dist(distrib[index]);
      const int middle=std::partition(dbuf+start,dbuf+end,dist) - dbuf;

      // const int middle=
      //   std::partition(dbuf+start,dbuf+end,[&](double a)
      //                  {return a<distrib[index];})
      //   - dbuf;

      std::thread lower(recursive_sort,start,middle,index-offset,offset/2,
                        depth+1,max_depth);
      std::thread upper(recursive_sort,middle,end,index+offset,offset/2,
                        depth+1,max_depth);
      lower.join(), upper.join();
    }
  else
    {
  // ippsSortRadixAscend_64f_I(dbuf+start,copy+start,end-start);

      c1=clock();

      double *dbuf_local(dbuf), *copy_local(copy);
      boost::uint64_t mask = (1 << step) - 1;
      int cnt[num_steps][mask+1];

      boost::uint64_t *ibuf = reinterpret_cast<boost::uint64_t *> (dbuf_local);

      for(int i=0;i<num_steps;++i)
        for(uint j=0;j<mask+1;++j)
          cnt[i][j]=0;

      for (int i = start; i < end; i++)
        {
          for (int w = start_step, v = 0; w < 64; w += step, v++)
            {
              int p = (~ibuf[i] >> w) & mask;
              (cnt[v][p])++;
            }
        }

      c2=clock();

      std::vector<int> sum(num_steps,0);
      for (uint i = 0; i <= mask; i++)
        {
          for (int w = start_step, v = 0; w < 64; w += step, v++)
            {
              int tmp = sum[v] + cnt[v][i];
              cnt[v][i] = sum[v];
              sum[v] = tmp;
            }
        }

      c3=clock();

      for (int w = start_step, v = 0; w < 64; w += step, v++)
        {
          ibuf = reinterpret_cast<boost::uint64_t *>(dbuf_local);

          for (int i = start; i < end; i++)
            {
              int p = (~ibuf[i] >> w) & mask;
              copy_local[start+((cnt[v][p])++)] = dbuf_local[i];
            }
          std::swap(copy_local,dbuf_local);
        }

      // Do the last set of reversals
      for (int p = start; p < end; p++)
        if (dbuf_local[p] >= 0.)
          {
            std::reverse(dbuf_local+p, dbuf_local + end);
            break;
          }

      c4=clock();

      // Insertion sort
      for (int i = start+1; i < end; i++) {
        double value = dbuf_local[i];
        if (value < dbuf_local[i - 1]) {
          dbuf_local[i] = dbuf_local[i - 1];
          int p = i - 1;
          for (; p > 0 && value < dbuf_local[p - 1]; p--)
            dbuf_local[p] = dbuf_local[p - 1];
          dbuf_local[p] = value;
        }
      }
      c5=clock();

    }
}


int main(int argc, char **argv) {
  size = atoi(argv[1]);
  copy = new double[size];

  dbuf = new double[size];
  FILE *f = fopen("gaussian.dat", "r");
  fread(dbuf, size, sizeof(double), f);
  fclose(f);

  clock_t c0 = clock();

  const int max_depth= (argc > 2) ? atoi(argv[2]) : 2;

  // ippsSortRadixAscend_64f_I(dbuf,copy,size);

  recursive_sort(0,size,31,16,0,max_depth);

  if(num_steps%2==1)
    std::swap(dbuf,copy);

  // for (int i=0; i<size-1; i++){
  //   if (dbuf[i]>dbuf[i+1])
  //     std::cout << "BAD "
  //               << i << " "
  //               << dbuf[i] << " "
  //               << dbuf[i+1] << " "
  //               << "\n";
  // }

  std::cout << "Finished after "
            << (double) (c1 - c0) / CLOCKS_PER_SEC << " "
            << (double) (c2 - c1) / CLOCKS_PER_SEC << " "
            << (double) (c3 - c2) / CLOCKS_PER_SEC << " "
            << (double) (c4 - c3) / CLOCKS_PER_SEC << " "
            << (double) (c5 - c4) / CLOCKS_PER_SEC << " "
            << "\n";

  // delete [] dbuf;
  // delete [] copy;
  return 0;
}

সম্পাদনা : আমি আলেকজান্দ্রুর ক্যাশে উন্নতিগুলি প্রয়োগ করেছি এবং এটি আমার মেশিনে প্রায় 30% সময় কেটে গেছে।

সম্পাদনা : এটি একটি পুনরাবৃত্তিক বাছাই কার্যকর করে, তাই এটি আলেকজান্দ্রুর 16 কোর মেশিনে ভালভাবে কাজ করা উচিত। এটি এর আলেকজান্দ্রুর শেষ উন্নতিও ব্যবহার করে এবং বিপরীতগুলির একটি অপসারণ করে। আমার জন্য, এটি একটি 20% উন্নতি দিয়েছে।

সম্পাদনা : 2 টি কোরের বেশি থাকাকালীন একটি সাইন বাগ সমাধান করা হয়েছে যা অদক্ষতার কারণ হয়ে দাঁড়ায়।

সম্পাদনা : ল্যাম্বদা সরানো হয়েছে, সুতরাং এটি জিসিসির পুরানো সংস্করণগুলির সাথে সংকলন করবে। এটিতে মন্তব্য করা আইপিপি কোড প্রকরণের অন্তর্ভুক্ত। আমি 16 টি কোর চালানোর জন্য ডকুমেন্টেশনও স্থির করেছি। আমি যতদূর বলতে পারি এটি দ্রুততম বাস্তবায়ন।

সম্পাদনা : স্টিপ ৮ না হলে একটি বাগ স্থির করা হয়েছে threads


খুশী হলাম। Radix বাছাই খুব বন্ধুত্বপূর্ণ ক্যাশে। পরিবর্তন করে আপনি আরও ভাল ফলাফল পেতে পারেন কিনা তা দেখুন step(11 আমার ল্যাপটপে অনুকূল ছিল)।
আলেকজান্দ্রু

আপনার একটি বাগ আছে: int cnt[mask]হওয়া উচিত int cnt[mask + 1]। ভাল ফলাফলের জন্য একটি নির্দিষ্ট মান ব্যবহার করুন int cnt[1 << 16]
আলেকজান্দ্রু

আমি বাড়িতে পৌঁছে যাওয়ার পরে এই সমস্ত সমাধানগুলি পরে চেষ্টা করব।
স্থির_আরত্তি

1.534s !!! আমি মনে করি আমাদের একটি নেতা রয়েছে :
ডি

@ স্ট্যাটিক_আরটি: আপনি কি আবার চেষ্টা করতে পারেন? এটি আপনি শেষবার চেষ্টা করার চেয়ে উল্লেখযোগ্যভাবে গতি পেয়েছে। আমার মেশিনে, এটি অন্য যে কোনও সমাধানের চেয়ে যথেষ্ট দ্রুত is
দামেস্ক স্টিল

2

আমি অনুমান করি এটি আপনি যা করতে চান তার উপর নির্ভর করে। আপনি যদি গৌসিয়ানদের একটি গোছা বাছাই করতে চান তবে এটি আপনাকে সাহায্য করবে না। তবে আপনি যদি সাজানো গৌসিয়ানদের একগুচ্ছ চান, এটি হবে। এমনকি যদি সমস্যাটি কিছুটা বাদ দেয় তবে আমি মনে করি বনাম প্রকৃত বাছাইয়ের রুটিনগুলির তুলনা করা আকর্ষণীয় হবে।

আপনি যদি কিছু দ্রুত করতে চান তবে কম করুন।

স্বাভাবিক বিতরণ থেকে একগুচ্ছ এলোমেলো নমুনা তৈরি করে বাছাই করার পরিবর্তে, আপনি বাছাই করা ক্রমে সাধারণ বিতরণ থেকে একগুচ্ছ নমুনা তৈরি করতে পারেন।

সাজানোর ক্রমে n ইউনিফর্ম এলোমেলো সংখ্যা তৈরি করতে আপনি এখানে সমাধানটি ব্যবহার করতে পারেন । তারপরে আপনি বিস্তৃত রূপান্তর নমুনার মাধ্যমে ইউনিফর্ম র্যান্ডম সংখ্যাকে সাধারণ বিতরণ থেকে সংখ্যায় রূপান্তর করতে সাধারণ বিতরণের বিপরীত সিডিএফ (scipy.stats.norm.ppf) ব্যবহার করতে পারেন ।

import scipy.stats
import random

# slightly modified from linked stackoverflow post
def n_random_numbers_increasing(n):
  """Like sorted(random() for i in range(n))),                                
  but faster because we avoid sorting."""
  v = 1.0
  while n:
    v *= random.random() ** (1.0 / n)
    yield 1 - v
    n -= 1

def n_normal_samples_increasing(n):
  return map(scipy.stats.norm.ppf, n_random_numbers_increasing(n))

আপনি যদি নিজের হাতটি ডিয়ার্টিয়ার পেতে চান তবে আমি অনুমান করতে পারি যে আপনি কোনও রকম পুনরাবৃত্ত পদ্ধতিটি ব্যবহার করে এবং পূর্ববর্তী ফলাফলটি আপনার প্রাথমিক অনুমান হিসাবে ব্যবহার করে অনেকগুলি বিপরীত সিডিএফ গণনা দ্রুত করতে সক্ষম হবেন। অনুমানগুলি খুব কাছাকাছি হতে চলেছে, সম্ভবত একটি একক পুনরাবৃত্তি আপনাকে দুর্দান্ত নির্ভুলতা দেবে।


2
উত্তম উত্তর, তবে এটি প্রতারণা হবে :) আমার প্রশ্নের ধারণাটি হল অ্যালগোরিদমগুলি বাছাই করার সময় প্রচুর মনোযোগ দেওয়া হয়েছে, বাছাইয়ের জন্য ডেটা সম্পর্কে পূর্ববর্তী জ্ঞানের ব্যবহার সম্পর্কে প্রায় সাহিত্য নেই, যদিও কয়েকটি কাগজপত্র রয়েছে ইস্যুটি মোকাবেলায় দুর্দান্ত লাভের কথা জানিয়েছেন। তাহলে দেখা যাক কী সম্ভব!

2

এই মেইন () এর সাথে গুভান্তের সমাধানটি পরিবর্তনের চেষ্টা করুন, 1/4 আইও পড়া শেষ হওয়ার সাথে সাথে এটি বাছাই শুরু হয়, এটি আমার পরীক্ষায় দ্রুত:

    static void Main(string[] args)
    {
        FileStream filestream = new FileStream(@"..\..\..\gaussian.dat", FileMode.Open, FileAccess.Read);
        doubles = new double[][] { new double[count / 4], new double[count / 4], new double[count / 4], new double[count / 4] };
        Thread[] threads = new Thread[4];

        for (int i = 0; i < 4; i++)
        {
            byte[] bytes = new byte[count * 4];
            filestream.Read(bytes, 0, count * 4);

            for (int j = 0; j < count / 4; j++)
            {
                doubles[i][j] = BitConverter.ToDouble(bytes, i * count/4 + j * 8);
            }

            threads[i] = new Thread(ThreadStart);
            waiting[i] = events[i] = new AutoResetEvent(false);
            threads[i].Start(i);    
        }

        WaitHandle.WaitAll(waiting);
        double[] left = Merge(doubles[0], doubles[1]);
        double[] right = Merge(doubles[2], doubles[3]);
        double[] result = Merge(left, right);
        Console.ReadKey();
    }
}

8.933s। কিছুটা দ্রুত :)

2

যেহেতু আপনি বিতরণটি জানেন, আমার ধারণাটি হবে কে বালতিগুলি তৈরি করা, প্রতিটি প্রত্যাশিত সংখ্যক উপাদান সহ (যেহেতু আপনি বিতরণটি জানেন, আপনি এটি গণনা করতে পারেন)। তারপরে ও (এন) সময়ে অ্যারেটি ঝাড়ু করুন এবং উপাদানগুলিকে তাদের বালতিতে রাখুন।

তারপরে একই সাথে বালতিগুলি বাছাই করুন। মনে করুন আপনার কাছে কে বালতি এবং এন উপাদান রয়েছে। একটি বালতি বাছাই করতে (এন / কে) এলজি (এন / কে) সময় নেবে। এখন ধরুন আপনার কাছে পি প্রসেসর রয়েছে যা আপনি ব্যবহার করতে পারেন। যেহেতু বালতিগুলি স্বাধীনভাবে বাছাই করা যায়, তাই আপনার সাথে সিলের গুণক (কে / পি) আছে। এটি এন + সিল (কে / পি) * (এন / কে) এলজি (এন / কে) এর একটি চূড়ান্ত রানটাইম দেয়, যদি আপনি কে ভালভাবে বেছে নেন তবে এন এলজি এন এর চেয়ে দ্রুত হওয়া ভাল should


আমি মনে করি এটিই সেরা সমাধান।
নিল জি

বালতিতে শেষ হওয়া উপাদানগুলির সংখ্যা আপনি ঠিক জানেন না, সুতরাং গণিতটি আসলে ভুল। বলা হচ্ছে, এটি আমার মনে হয় একটি ভাল উত্তর।
poulejapon

@ পোয়েজাপোন: আপনি ঠিক বলেছেন।
নীল জি

এই উত্তরটি শোনাচ্ছে সত্যিই চমৎকার। সমস্যাটি হচ্ছে - এটি আসলে দ্রুত নয়। আমি এটি C99 এ প্রয়োগ করেছি (আমার উত্তর দেখুন), এবং এটি অবশ্যই সহজেই মারধর করে std::sort(), তবে এটি আলেকজান্দ্রুর র‌্যাডিক্সোর্ট সমাধানের চেয়ে ধীর গতিতে।
সোভেন মারনাচ

2

একটি নিম্ন-স্তরের অপ্টিমাইজেশন ধারণাটি একটি এসএসই রেজিস্ট্রারে দুটি ডাবল ফিট করা উচিত, তাই প্রতিটি থ্রেড একবারে দুটি আইটেমের সাথে কাজ করবে। কিছু অ্যালগরিদমের জন্য এটি জটিল হতে পারে।

আরেকটি কাজ হ'ল ক্যাশে-বান্ধব অংশগুলিতে অ্যারে বাছাই করা, তারপরে ফলাফলগুলি মার্জ করা। দুটি স্তর ব্যবহার করা উচিত: উদাহরণস্বরূপ, প্রথমে এল 1 এর জন্য 4 কেবি এবং এল 2 এর জন্য 64 কেবি।

এটি খুব ক্যাশে-বান্ধব হওয়া উচিত, যেহেতু বালতি সাজানো ক্যাশেটির বাইরে যাবে না, এবং চূড়ান্ত মার্জটি মেমরির ধারাবাহিকভাবে চলবে।

এই দিনগুলিতে গণনা মেমরি অ্যাক্সেসের চেয়ে অনেক সস্তা। তবে আমাদের কাছে প্রচুর পরিমাণে আইটেম রয়েছে, তাই কম-জটিলতা-অ-ক্যাশে-সচেতন সংস্করণের তুলনায় বোবা ক্যাশে-সচেতন বাছাই ধীরে ধীরে যখন অ্যারে আকার হয় তা বলা শক্ত।

তবে আমি উইন্ডোতে (ভিসি ++) এটি করার কারণে আমি উপরেরটির বাস্তবায়ন সরবরাহ করব না।


2

এখানে একটি লিনিয়ার স্ক্যান বালতি সাজানোর বাস্তবায়ন। আমি মনে করি এটি Radix বাছাই ব্যতীত সমস্ত বর্তমান একক থ্রেডযুক্ত বাস্তবায়নগুলির চেয়ে দ্রুত। যদি আমি সিডিএফ যথাযথভাবে যথাযথভাবে অনুমান করি তবে এটির লিনিয়ার প্রত্যাশিত চলমান সময় থাকা উচিত (আমি ওয়েবে যে মূল্যবোধগুলি পেয়েছি তার মধ্যে লিনিয়ার ইন্টারপোলেশন ব্যবহার করছি) এবং অতিরিক্ত কোনও স্ক্যানিং ঘটায় এমন কোনও ভুল করেনি:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <ctime>

using std::fill;

const double q[] = {
  0.0,
  9.865E-10,
  2.8665150000000003E-7,
  3.167E-5,
  0.001349898,
  0.022750132,
  0.158655254,
  0.5,
  0.8413447460000001,
  0.9772498679999999,
  0.998650102,
  0.99996833,
  0.9999997133485,
  0.9999999990134999,
  1.0,
};
int main(int argc, char** argv) {
  if (argc <= 1)
    return puts("No argument!");
  unsigned count = atoi(argv[1]);
  unsigned count2 = 3 * count;

  bool *ba = new bool[count2 + 1000];
  fill(ba, ba + count2 + 1000, false);
  double *a = new double[count];
  double *c = new double[count2 + 1000];

  FILE *f = fopen("gaussian.dat", "rb");
  if (fread(a, 8, count, f) != count)
    return puts("fread failed!");
  fclose(f);

  int i;
  int j;
  bool s;
  int t;
  double z;
  double p;
  double d1;
  double d2;
  for (i = 0; i < count; i++) {
    s = a[i] < 0;
    t = a[i];
    if (s) t--;
    z = a[i] - t;
    t += 7;
    if (t < 0) {
      t = 0;
      z = 0;
    } else if (t >= 14) {
      t = 13;
      z = 1;
    }
    p = q[t] * (1 - z) + q[t + 1] * z;
    j = count2 * p;
    while (ba[j] && c[j] < a[i]) {
      j++;
    }
    if (!ba[j]) {
      ba[j] = true;
      c[j] = a[i];
    } else {
      d1 = c[j];
      c[j] = a[i];
      j++;
      while (ba[j]) {
        d2 = c[j];
        c[j] = d1;
        d1 = d2;
        j++;
      }
      c[j] = d1;
      ba[j] = true;
    }
  }
  i = 0;
  int max = count2 + 1000;
  for (j = 0; j < max; j++) {
    if (ba[j]) {
      a[i++] = c[j];
    }
  }
  // for (i = 0; i < count; i += 1) {
  //   printf("here %f\n", a[i]);
  // }
  return 0;
}

1
আমি বাড়িতে এলে আজ পরে এটি চেষ্টা করব। এর মধ্যে, আমি কি বলতে পারি যে আপনার কোডটি খুব কুৎসিত? :-D
অচল_আরত্তি

3.071s! একক থ্রেডযুক্ত সমাধানের জন্য খারাপ নয়!
স্থির_আরত্তি

2

আমি জানি না, কেন আমি আমার আগের পোস্টটি সম্পাদনা করতে পারছি না, সুতরাং 0,2 সেকেন্ড দ্রুত (তবে সিপিইউ সময়ের (ব্যবহারকারী) প্রায় 1,5 গুলি দ্রুত) এর নতুন সংস্করণটি এখানে। এই দ্রবণটিতে 2 টি প্রোগ্রাম রয়েছে, প্রথমে বালতি সাজানোর জন্য স্বাভাবিক বিতরণের জন্য কোয়ান্টাইলগুলি প্রাক্কলকুলেট করে এবং এটি টেবিলের মধ্যে সংরক্ষণ করে, টি [ডাবল * স্কেল] = বালতি সূচক, যেখানে স্কেল এমন কিছু স্বেচ্ছাসেবী সংখ্যা যা দ্বিগুণকে সম্ভব করে দেয়। তারপরে মূল প্রোগ্রামটি সঠিক বালতিতে ডাবল রাখার জন্য এই ডেটা ব্যবহার করতে পারে। এটির একটি অসুবিধা রয়েছে, যদি ডেটা গাউস না হয় তবে এটি সঠিকভাবে কাজ করবে না (এবং সাধারণ বিতরণের জন্য ভুলভাবে কাজ করার প্রায় শূন্যের সুযোগও রয়েছে), তবে বিশেষ ক্ষেত্রে সংশোধন করা সহজ এবং দ্রুত (কেবল বালতি চেকের সংখ্যা এবং স্ট্যান্ডে পড়ে যাওয়া) ::সাজান()).

সংকলন: জি ++ => http://pastebin.com/WG7pZEzH সহায়ক প্রোগ্রাম

g ++ -std = c ++ 0x -O3 -march = नेटটি -প্রেড => http://pastebin.com/T3yzViZP মূল বাছাইকরণ প্রোগ্রাম


1.621s! আমি মনে করি আপনিই নেতা, তবে আমি এই সমস্ত উত্তর দিয়ে দ্রুত ট্র্যাক হারাচ্ছি :)
স্ট্যাটিক_আরটি

2

এখানে আরও একটি অনুক্রমিক সমাধান। এইটি উপাদানগুলিকে স্বাভাবিক বিতরণ করা হয় এবং এই ধারণাটি সাধারণত রৈখিক সময়ের কাছাকাছি বাছাইয়ের জন্য প্রযোজ্য বলে আমি মনে করি।

অ্যালগরিদমটি এরকম:

  • আনুমানিক সিডিএফ ( phi()বাস্তবায়নের কাজ দেখুন)
  • সকল উপাদানগুলির জন্য বাছাই করা অ্যারেতে আনুমানিক অবস্থান গণনা করুন: size * phi(x)
  • উপাদানগুলিকে তাদের চূড়ান্ত অবস্থানের নিকটে একটি নতুন অ্যারেতে রাখুন
    • আমার বাস্তবায়নের গন্তব্য অ্যারেতে কিছু ফাঁক রয়েছে তাই serোকানোর সময় আমাকে খুব বেশি উপাদান স্থানান্তর করতে হবে না।
  • চূড়ান্ত উপাদানগুলি বাছাই করতে সন্নিবেশকারী ব্যবহার করুন (চূড়ান্ত অবস্থানের দূরত্ব যদি ধ্রুবকের চেয়ে ছোট হয় তবে সন্নিবেশকারী রৈখিক হয়)।

দুর্ভাগ্যক্রমে, লুকানো ধ্রুবকটি বেশ বড় এবং এই দ্রবণটি রেডিক্স সাজানোর অ্যালগরিদমের দ্বিগুণ ধীর।


1
2.470s! খুব সুন্দর ধারণা। ধারণাগুলি আকর্ষণীয় হলে সমাধানটি দ্রুততম নয় তা
বিবেচ্য নয়

1
এটি আমার মতো একই, তবে আরও ভাল ক্যাশে পারফরম্যান্সের জন্য ফাই কম্পিউটেশন এবং শিফ্টগুলি একসাথে গ্রুপিং করা, তাই না?
জন্ড্রি

@ জন্ডেরি: আমি আপনার সমাধানটিকে সমর্থন করেছি, এখন আমি বুঝতে পারি যে এটি কী করে। আপনার ধারণা চুরি করার অর্থ নয়। আমি আপনার বাস্তবায়নটিকে আমার (অফিশিয়াল) পরীক্ষার সেটটিতে
আলেকজান্দ্রু

2

ইন্টেলের থ্রেডেড বিল্ডিং ব্লকগুলি ব্যবহার করে আমার ব্যক্তিগত প্রিয় ইতিমধ্যে পোস্ট করা হয়েছে, তবে এখানে জেডিকে 7 এবং এর নতুন কাঁটাচামচ / যোগদান এপিআই ব্যবহার করে একটি অপরিশোধিত সমান্তরাল সমাধান রয়েছে:

import java.io.FileInputStream;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.concurrent.*;
import static java.nio.channels.FileChannel.MapMode.READ_ONLY;
import static java.nio.ByteOrder.LITTLE_ENDIAN;


/**
 * 
 * Original Quicksort: https://github.com/pmbauer/parallel/tree/master/src/main/java/pmbauer/parallel
 *
 */
public class ForkJoinQuicksortTask extends RecursiveAction {

    public static void main(String[] args) throws Exception {

        double[] array = new double[Integer.valueOf(args[0])];

        FileChannel fileChannel = new FileInputStream("gaussian.dat").getChannel();
        fileChannel.map(READ_ONLY, 0, fileChannel.size()).order(LITTLE_ENDIAN).asDoubleBuffer().get(array);

        ForkJoinPool mainPool = new ForkJoinPool();

        System.out.println("Starting parallel computation");

        mainPool.invoke(new ForkJoinQuicksortTask(array));        
    }

    private static final long serialVersionUID = -642903763239072866L;
    private static final int SERIAL_THRESHOLD = 0x1000;

    private final double a[];
    private final int left, right;

    public ForkJoinQuicksortTask(double[] a) {this(a, 0, a.length - 1);}

    private ForkJoinQuicksortTask(double[] a, int left, int right) {
        this.a = a;
        this.left = left;
        this.right = right;
    }

    @Override
    protected void compute() {
        if (right - left < SERIAL_THRESHOLD) {
            Arrays.sort(a, left, right + 1);
        } else {
            int pivotIndex = partition(a, left, right);
            ForkJoinTask<Void> t1 = null;

            if (left < pivotIndex)
                t1 = new ForkJoinQuicksortTask(a, left, pivotIndex).fork();
            if (pivotIndex + 1 < right)
                new ForkJoinQuicksortTask(a, pivotIndex + 1, right).invoke();

            if (t1 != null)
                t1.join();
        }
    }

    public static int partition(double[] a, int left, int right) {
        // chose middle value of range for our pivot
        double pivotValue = a[left + (right - left) / 2];

        --left;
        ++right;

        while (true) {
            do
                ++left;
            while (a[left] < pivotValue);

            do
                --right;
            while (a[right] > pivotValue);

            if (left < right) {
                double tmp = a[left];
                a[left] = a[right];
                a[right] = tmp;
            } else {
                return right;
            }
        }
    }    
}

গুরুত্বপূর্ণ অস্বীকৃতি : আমি কাঁটাচামচ / যোগ দেওয়ার জন্য দ্রুত সাজানোর মানিয়ে নিয়েছি: https://github.com/pmbauer/parallel/tree/master/src/main/java/pmbauer/parallel

এটি চালানোর জন্য আপনার জেডিকে 7 (http://jdk7.java.net/download.html) এর বিটা বিল্ড দরকার।

আমার 2.93Ghz কোয়াড কোর আই 7 (ওএস এক্স) এ:

পাইথন রেফারেন্স

time python sort.py 50000000
sorting...

real    1m13.885s
user    1m11.942s
sys     0m1.935s

জাভা জেডিকে 7 কাঁটাচামচ / যোগদান করুন

time java ForkJoinQuicksortTask 50000000
Starting parallel computation

real    0m2.404s
user    0m10.195s
sys     0m0.347s

আমি সমান্তরাল পাঠ এবং বাইটসকে ডাবলসে রূপান্তর করার জন্য কিছু পরীক্ষা-নিরীক্ষা করার চেষ্টা করেছি, কিন্তু আমি সেখানে কোনও পার্থক্য দেখলাম না।

হালনাগাদ:

যদি কেউ ডেটার সমান্তরাল লোডিং নিয়ে পরীক্ষা করতে চায় তবে সমান্তরাল লোডিং সংস্করণটি নীচে রয়েছে। তাত্ত্বিকভাবে এটি এটিকে কিছুটা দ্রুত এগিয়ে যেতে পারে, যদি আপনার আইও ডিভাইসে পর্যাপ্ত পরিমাণে সমান্তরাল ক্ষমতা থাকে (এসএসডি সাধারণত হয়)। বাইটস থেকে ডাবল তৈরিতে কিছু ওভারহেডও রয়েছে, যাতে সমান্তরালেও সম্ভবত এটি দ্রুত যেতে পারে। আমার সিস্টেমে (উবুন্টু 10.10 / নেহলেম কোয়াড / ইন্টেল এক্স 25 এম এসএসডি, এবং ওএস এক্স 10.6 / আই 7 কোয়াড / স্যামসাং এসএসডি) আমি কোনও বাস্তব পার্থক্য দেখতে পাইনি।

import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.channels.FileChannel.MapMode.READ_ONLY;

import java.io.FileInputStream;
import java.nio.DoubleBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveAction;


/**
 *
 * Original Quicksort: https://github.com/pmbauer/parallel/tree/master/src/main/java/pmbauer/parallel
 *
 */
public class ForkJoinQuicksortTask extends RecursiveAction {

   public static void main(String[] args) throws Exception {

       ForkJoinPool mainPool = new ForkJoinPool();

       double[] array = new double[Integer.valueOf(args[0])];
       FileChannel fileChannel = new FileInputStream("gaussian.dat").getChannel();
       DoubleBuffer buffer = fileChannel.map(READ_ONLY, 0, fileChannel.size()).order(LITTLE_ENDIAN).asDoubleBuffer();

       mainPool.invoke(new ReadAction(buffer, array, 0, array.length));
       mainPool.invoke(new ForkJoinQuicksortTask(array));
   }

   private static final long serialVersionUID = -642903763239072866L;
   private static final int SERIAL_THRESHOLD = 0x1000;

   private final double a[];
   private final int left, right;

   public ForkJoinQuicksortTask(double[] a) {this(a, 0, a.length - 1);}

   private ForkJoinQuicksortTask(double[] a, int left, int right) {
       this.a = a;
       this.left = left;
       this.right = right;
   }

   @Override
   protected void compute() {
       if (right - left < SERIAL_THRESHOLD) {
           Arrays.sort(a, left, right + 1);
       } else {
           int pivotIndex = partition(a, left, right);
           ForkJoinTask<Void> t1 = null;

           if (left < pivotIndex)
               t1 = new ForkJoinQuicksortTask(a, left, pivotIndex).fork();
           if (pivotIndex + 1 < right)
               new ForkJoinQuicksortTask(a, pivotIndex + 1, right).invoke();

           if (t1 != null)
               t1.join();
       }
   }

   public static int partition(double[] a, int left, int right) {
       // chose middle value of range for our pivot
       double pivotValue = a[left + (right - left) / 2];

       --left;
       ++right;

       while (true) {
           do
               ++left;
           while (a[left] < pivotValue);

           do
               --right;
           while (a[right] > pivotValue);

           if (left < right) {
               double tmp = a[left];
               a[left] = a[right];
               a[right] = tmp;
           } else {
               return right;
           }
       }
   }

}

class ReadAction extends RecursiveAction {

   private static final long serialVersionUID = -3498527500076085483L;

   private final DoubleBuffer buffer;
   private final double[] array;
   private final int low, high;

   public ReadAction(DoubleBuffer buffer, double[] array, int low, int high) {
       this.buffer = buffer;
       this.array = array;
       this.low = low;
       this.high = high;
   }

   @Override
   protected void compute() {
       if (high - low < 100000) {
           buffer.position(low);
           buffer.get(array, low, high-low);
       } else {
           int middle = (low + high) >>> 1;

           invokeAll(new ReadAction(buffer.slice(), array, low, middle),  new ReadAction(buffer.slice(), array, middle, high));
       }
   }
}

Update2:

আমি একটি নির্দিষ্ট পরিমাণে কোর সেট করতে সামান্য পরিবর্তন নিয়ে আমাদের 12 টি কোর দেব মেশিনগুলির একটিতে কোডটি কার্যকর করেছিলাম। এটি নিম্নলিখিত ফলাফল দিয়েছে:

Cores  Time
1      7.568s
2      3.903s
3      3.325s
4      2.388s
5      2.227s
6      1.956s
7      1.856s
8      1.827s
9      1.682s
10     1.698s
11     1.620s
12     1.503s

এই সিস্টেমে আমি পাইথন সংস্করণটিও চেষ্টা করেছিলাম যা 1 এম 2.994 সেকেন্ড এবং জাজারেকের সি ++ সংস্করণটি 1.925 সেকেন্ড নিয়েছে (কোনও কারণে জাজেরেকের সি ++ সংস্করণটি স্ট্যাটিক_আরটিটির কম্পিউটারে তুলনামূলকভাবে দ্রুত চলতে দেখা যায়)।

আমি যদি ফাইলের আকারটি 100,000,000 ডাবল করে দ্বিগুণ করি তবে কী ঘটেছিল তা আমি চেষ্টা করেছিলাম:

Cores  Time
1      15.056s
2      8.116s
3      5.925s
4      4.802s
5      4.430s
6      3.733s
7      3.540s
8      3.228s
9      3.103s
10     2.827s
11     2.784s
12     2.689s

এই ক্ষেত্রে, জাজেরেকের সি ++ সংস্করণটি 3.968 এর মধ্যে নিয়েছে। পাইথন এখানে খুব দীর্ঘ সময় নিয়েছে।

150,000,000 ডাবলস:

Cores  Time
1      23.295s
2      12.391s
3      8.944s
4      6.990s
5      6.216s
6      6.211s
7      5.446s
8      5.155s
9      4.840s
10     4.435s
11     4.248s
12     4.174s

এই ক্ষেত্রে, জাজেরেকের সি ++ সংস্করণটি ছিল 6.044 এস। আমি পাইথনের চেষ্টাও করি নি।

সি ++ সংস্করণ এর ফলাফলগুলির সাথে খুব সামঞ্জস্যপূর্ণ, যেখানে জাভা একটু দুলছে। সমস্যাটি বড় হওয়ার আগে প্রথমে এটি আরও বেশি দক্ষ হয়, তবে তারপরে আবার কম দক্ষ।


1
এই কোডটি আমার জন্য দ্বিগুণ মানগুলি সঠিকভাবে পার্স করে না। জাভা 7 ফাইল থেকে মানগুলি সঠিকভাবে পার্স করা প্রয়োজন?
জন্ড্রি

1
আহ, বোকা আমাকে। স্থানীয়ভাবে আমি একাধিক লাইন থেকে আইও কোডটি রিফেক্টর করার পরে আমি আবার এন্ডিয়েনস সেট করতে ভুলে গিয়েছিলাম। জাভা 7 সাধারণত প্রয়োজন হবে, যদি না আপনি কাঁটাচামড়া যোগ করেন / অবশ্যই অবশ্যই জাভা 6 তে আলাদাভাবে যোগদান না করেন।
আরজান

আমার মেশিনে 3.411 এস। খারাপ না, কিন্তু চেয়ে koumes21 এর জাভা সমাধান :) ধীর
static_rtti

1
আমার সিস্টেমে তুলনামূলক পার্থক্য কী তা দেখতে আমি এখানে স্থানীয়ভাবে কৌম 2121 এর সমাধান চেষ্টা করব। যাইহোক, কুমেস 21 থেকে 'হারাতে' কোনও লজ্জা নেই কারণ এটি অনেক বেশি চালাক সমাধান। এটি একটি কাঁটাচামচ / জয়েন পুলে ফেলে দেওয়া প্রায় স্ট্যান্ডার্ড দ্রুত-সাজান;)
আরজান

1

চিরাচরিত pthreads ব্যবহার করে একটি সংস্করণ। গুভান্তের উত্তর থেকে মার্জ করার কোড। সঙ্গে সংকলন g++ -O3 -pthread

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

static unsigned int nthreads = 4;
static unsigned int size = 50000000;

typedef struct {
  double *array;
  int size;
} array_t;


void 
merge(double *left, int leftsize,
      double *right, int rightsize,
      double *result)
{
  int l = 0, r = 0, insertat = 0;
  while (l < leftsize && r < rightsize) {
    if (left[l] < right[r])
      result[insertat++] = left[l++];
    else
      result[insertat++] = right[r++];
  }

  while (l < leftsize) result[insertat++] = left[l++];
  while (r < rightsize) result[insertat++] = right[r++];
}


void *
run_thread(void *input)
{
  array_t numbers = *(array_t *)input;
  std::sort(numbers.array, numbers.array+numbers.size); 
  pthread_exit(NULL);
}

int 
main(int argc, char **argv) 
{
  double *numbers = (double *) malloc(size * sizeof(double));

  FILE *f = fopen("gaussian.dat", "rb");
  if (fread(numbers, sizeof(double), size, f) != size)
    return printf("Reading gaussian.dat failed");
  fclose(f);

  array_t worksets[nthreads];
  int worksetsize = size / nthreads;
  for (int i = 0; i < nthreads; i++) {
    worksets[i].array=numbers+(i*worksetsize);
    worksets[i].size=worksetsize;
  }

  pthread_attr_t attributes;
  pthread_attr_init(&attributes);
  pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE);

  pthread_t threads[nthreads];
  for (int i = 0; i < nthreads; i++) {
    pthread_create(&threads[i], &attributes, &run_thread, &worksets[i]);
  }

  for (int i = 0; i < nthreads; i++) {
    pthread_join(threads[i], NULL);
  }

  double *tmp = (double *) malloc(size * sizeof(double));
  merge(numbers, worksetsize, numbers+worksetsize, worksetsize, tmp);
  merge(numbers+(worksetsize*2), worksetsize, numbers+(worksetsize*3), worksetsize, tmp+(size/2));
  merge(tmp, worksetsize*2, tmp+(size/2), worksetsize*2, numbers);

  /*
  printf("Verifying result..\n");
  for (int i = 0; i < size - 1; i++) {
    if (numbers[i] > numbers[i+1])
      printf("Result is not correct\n");
  }
  */

  pthread_attr_destroy(&attributes);
  return 0;
}  

আমার ল্যাপটপে আমি নিম্নলিখিত ফলাফলগুলি পাই:

real    0m6.660s
user    0m9.449s
sys     0m1.160s

1

এখানে একটি ক্রমিক C99 বাস্তবায়ন যা জ্ঞাত বিতরণটি সত্যই ব্যবহার করার চেষ্টা করে। এটি মূলত বিতরণের তথ্য ব্যবহার করে বালতি বাছাইয়ের একক রাউন্ড করে, তারপরে বালতিটির সীমাতে অভিন্ন বিতরণ ধরে প্রতিটি বালতিতে কয়েক চক্রের কুইকোর্ট এবং শেষ পর্যন্ত পরিবর্তিত বাছাই করা বাছাই করে তথ্যটিকে আসল বাফারে আবার অনুলিপি করতে পারে। কুইকোর্টটি বিভক্ত পয়েন্টগুলি মুখস্থ করে, তাই বাছাই বাছাইয়ের জন্য কেবল ছোট কঙ্কগুলিতে পরিচালনা করা দরকার। এবং এত জটিলতা সত্ত্বেও (কারণ?), এটি এমনকি সত্যই দ্রুত নয়।

দ্রুততর মূল্যায়ন করতে, মানগুলি কয়েকটি পয়েন্টে নমুনাযুক্ত হয় এবং পরে কেবল লিনিয়ার ইন্টারপোলেশন ব্যবহৃত হয়। এটি যতক্ষণ না strictly সঠিকভাবে নির্ধারণ করা হয় ততক্ষণ তা বিবেচনা করে না as

বিন আকারগুলি এমনভাবে বেছে নেওয়া হয় যে বিনের ওভারফ্লো হওয়ার সম্ভাবনা নগণ্য। আরও স্পষ্টভাবে, বর্তমান প্যারামিটারগুলির সাথে, 50000000 উপাদানগুলির একটি ডেটাসেট একটি বিনের ওভারফ্লোতে কারণ হওয়ার সম্ভাবনা 3.65e-09। (এই ব্যবহার নির্ণিত করা যেতে পারে বেঁচে থাকার ফাংশন এর পইসন বিতরণের ।)

সংকলন করতে, দয়া করে ব্যবহার করুন

gcc -std=c99 -msse3 -O3 -ffinite-math-only

যেহেতু অন্যান্য সমাধানগুলির তুলনায় যথেষ্ট বেশি গণনা রয়েছে, তাই কমপক্ষে যুক্তিসঙ্গত দ্রুত করার জন্য এই সংকলক পতাকাগুলির প্রয়োজন। ছাড়া -msse3থেকে ধর্মান্তর doubleকরার intসত্যিই ধীর হয়ে পড়ে। যদি আপনার আর্কিটেকচার এসএসই 3 সমর্থন করে না, তবে এই রূপান্তরগুলি lrint()ফাংশনটি ব্যবহার করেও করা যেতে পারে ।

কোডটি বরং কুৎসিত - এটি "যুক্তিসঙ্গত পাঠযোগ্য" হওয়ার প্রয়োজনীয়তা পূরণ করে কিনা তা নিশ্চিত নন ...

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

#define N 50000000
#define BINSIZE 720
#define MAXBINSIZE 880
#define BINCOUNT (N / BINSIZE)
#define SPLITS 64
#define PHI_VALS 513

double phi_vals[PHI_VALS];

int bin_index(double x)
{
    double y = (x + 8.0) * ((PHI_VALS - 1) / 16.0);
    int interval = y;
    y -= interval;
    return (1.0 - y) * phi_vals[interval] + y * phi_vals[interval + 1];
}

double bin_value(int bin)
{
    int left = 0;
    int right = PHI_VALS - 1;
    do
    {
        int centre = (left + right) / 2;
        if (bin < phi_vals[centre])
            right = centre;
        else
            left = centre;
    } while (right - left > 1);
    double frac = (bin - phi_vals[left]) / (phi_vals[right] - phi_vals[left]);
    return (left + frac) * (16.0 / (PHI_VALS - 1)) - 8.0;
}

void gaussian_sort(double *restrict a)
{
    double *b = malloc(BINCOUNT * MAXBINSIZE * sizeof(double));
    double **pos = malloc(BINCOUNT * sizeof(double*));
    for (size_t i = 0; i < BINCOUNT; ++i)
        pos[i] = b + MAXBINSIZE * i;
    for (size_t i = 0; i < N; ++i)
        *pos[bin_index(a[i])]++ = a[i];
    double left_val, right_val = bin_value(0);
    for (size_t bin = 0, i = 0; bin < BINCOUNT; ++bin)
    {
        left_val = right_val;
        right_val = bin_value(bin + 1);
        double *splits[SPLITS + 1];
        splits[0] = b + bin * MAXBINSIZE;
        splits[SPLITS] = pos[bin];
        for (int step = SPLITS; step > 1; step >>= 1)
            for (int left_split = 0; left_split < SPLITS; left_split += step)
            {
                double *left = splits[left_split];
                double *right = splits[left_split + step] - 1;
                double frac = (double)(left_split + (step >> 1)) / SPLITS;
                double pivot = (1.0 - frac) * left_val + frac * right_val;
                while (1)
                {
                    while (*left < pivot && left <= right)
                        ++left;
                    while (*right >= pivot && left < right)
                        --right;
                    if (left >= right)
                        break;
                    double tmp = *left;
                    *left = *right;
                    *right = tmp;
                    ++left;
                    --right;
                }
                splits[left_split + (step >> 1)] = left;
            }
        for (int left_split = 0; left_split < SPLITS; ++left_split)
        {
            double *left = splits[left_split];
            double *right = splits[left_split + 1] - 1;
            while (left <= right)
            {
                double *min = left;
                for (double *tmp = left + 1; tmp <= right; ++tmp)
                    if (*tmp < *min)
                        min = tmp;
                a[i++] = *min;
                *min = *right--;
            }
        }
    }
    free(b);
    free(pos);
}

int main()
{
    double *a = malloc(N * sizeof(double));
    FILE *f = fopen("gaussian.dat", "rb");
    assert(fread(a, sizeof(double), N, f) == N);
    fclose(f);
    for (int i = 0; i < PHI_VALS; ++i)
    {
        double x = (i * (16.0 / PHI_VALS) - 8.0) / sqrt(2.0);
        phi_vals[i] =  (erf(x) + 1.0) * 0.5 * BINCOUNT;
    }
    gaussian_sort(a);
    free(a);
}

4.098s! এটি সংকলনের জন্য আমাকে এলএএম যুক্ত করতে হয়েছিল (এরফের জন্য)।
স্থির_আরত্তি

1
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <memory.h>
#include <algorithm>

// maps [-inf,+inf] to (0,1)
double normcdf(double x) {
        return 0.5 * (1 + erf(x * M_SQRT1_2));
}

int calcbin(double x, int bins) {
        return (int)floor(normcdf(x) * bins);
}

int *docensus(int bins, int n, double *arr) {
        int *hist = calloc(bins, sizeof(int));
        int i;
        for(i = 0; i < n; i++) {
                hist[calcbin(arr[i], bins)]++;
        }
        return hist;
}

void partition(int bins, int *orig_counts, double *arr) {
        int *counts = malloc(bins * sizeof(int));
        memcpy(counts, orig_counts, bins*sizeof(int));
        int *starts = malloc(bins * sizeof(int));
        int b, i;
        starts[0] = 0;
        for(i = 1; i < bins; i++) {
                starts[i] = starts[i-1] + counts[i-1];
        }
        for(b = 0; b < bins; b++) {
                while (counts[b] > 0) {
                        double v = arr[starts[b]];
                        int correctbin;
                        do {
                                correctbin = calcbin(v, bins);
                                int swappos = starts[correctbin];
                                double tmp = arr[swappos];
                                arr[swappos] = v;
                                v = tmp;
                                starts[correctbin]++;
                                counts[correctbin]--;
                        } while (correctbin != b);
                }
        }
        free(counts);
        free(starts);
}


void sortbins(int bins, int *counts, double *arr) {
        int start = 0;
        int b;
        for(b = 0; b < bins; b++) {
                std::sort(arr + start, arr + start + counts[b]);
                start += counts[b];
        }
}


void checksorted(double *arr, int n) {
        int i;
        for(i = 1; i < n; i++) {
                if (arr[i-1] > arr[i]) {
                        printf("out of order at %d: %lf %lf\n", i, arr[i-1], arr[i]);
                        exit(1);
                }
        }
}


int main(int argc, char *argv[]) {
        if (argc == 1 || argv[1] == NULL) {
                printf("Expected data size as argument\n");
                exit(1);
        }
        int n = atoi(argv[1]);
        const int cachesize = 128 * 1024; // a guess
        int bins = (int) (1.1 * n * sizeof(double) / cachesize);
        if (argc > 2) {
                bins = atoi(argv[2]);
        }
        printf("Using %d bins\n", bins);
        FILE *f = fopen("gaussian.dat", "rb");
        if (f == NULL) {
                printf("Couldn't open gaussian.dat\n");
                exit(1);
        }
        double *arr = malloc(n * sizeof(double));
        fread(arr, sizeof(double), n, f);
        fclose(f);

        int *counts = docensus(bins, n, arr);
        partition(bins, counts, arr);
        sortbins(bins, counts, arr);
        checksorted(arr, n);

        return 0;
}

এটি প্রতিটি উপাদানকে একটি বিনে যথাযথভাবে স্থাপন করার জন্য ইরফ () ব্যবহার করে তারপরে প্রতিটি বিনকে সাজায়। এটি অ্যারে পুরোপুরি স্থানে রাখে।

প্রথম পাস: ডসেন্সাস () প্রতিটি বিনের উপাদানগুলির সংখ্যা গণনা করে।

দ্বিতীয় পাস: পার্টিশন () অ্যারেটিকে অনুমতি দেয়, প্রতিটি উপাদানকে তার সঠিক বিনে রাখে

তৃতীয় পাস: সাজ্টবিন () প্রতিটি বিনের উপর একটি Qsort সম্পাদন করে।

এটি এক ধরণের নিষ্পাপ, এবং প্রতিটি মানের জন্য দুবার ব্যয়বহুল এরফ () ফাংশন বলে। প্রথম এবং তৃতীয় পাসগুলি সম্ভাব্য সমান্তরাল। দ্বিতীয়টি অত্যন্ত সিরিয়াল এবং সম্ভবত এটির অত্যন্ত এলোমেলো মেমরি অ্যাক্সেস নিদর্শন দ্বারা ধীর হয়ে যায়। মেমরি গতির অনুপাতের সিপিইউ পাওয়ারের উপর নির্ভর করে প্রতিটি ডাবলের বিন নম্বর ক্যাশে করা উপযুক্ত হবে।

এই প্রোগ্রামটি আপনাকে ব্যবহার করার জন্য বিনের সংখ্যা চয়ন করতে দেয়। কমান্ড লাইনে কেবল একটি দ্বিতীয় সংখ্যা যুক্ত করুন। আমি এটি জিসিসি -৩৩ দিয়ে সংকলিত করেছি, তবে আমার মেশিনটি এতটাই দুর্বল আমি আপনাকে কোনও ভাল পারফরম্যান্স নম্বর বলতে পারছি না।

সম্পাদনা: ছোঁ! আমার সি প্রোগ্রামটি std :: সাজান্ট ব্যবহার করে যাদুতে সি ++ প্রোগ্রামে রূপান্তরিত হয়েছে!


আপনি দ্রুত stdnormal_cdf এর জন্য phi ব্যবহার করতে পারেন ।
আলেকজান্দ্রু

আমার প্রায় কতটি বিন্দু লাগানো উচিত?
স্থির_আরত্তি

@ আলেকজান্দ্রু: আমি নর্ম্মসিডিএফ-তে একটি লম্বা রৈখিক লিনিয়ার আনুমানিক যোগ করেছি এবং কেবল প্রায় 5% গতি অর্জন করেছি।
frud

@ স্ট্যাটিক_আরটি: আপনার কোনও দরকার নেই। ডিফল্টরূপে, কোড বিনগুলি গণনাটি বেছে নেয় যাতে গড় বিনের আকার 128kb এর 10/11 হয়। খুব কম বিন এবং আপনি ভাগ করার সুবিধা পাবেন না। প্রচুর পরিমাণে এবং ক্যাশে উপচে পড়ার কারণে পার্টিশন পর্ব বগল।

10.6s! আমি বিনের সংখ্যার সাথে কিছুটা খেলতে চেষ্টা করেছি এবং 5000 সহ সেরা ফলাফল পেয়েছি (3356 এর ডিফল্ট মানের চেয়ে কিছুটা বেশি)। আমি অবশ্যই বলতে পারি যে আপনার সমাধানের জন্য আমি আরও ভাল পারফরম্যান্সের প্রত্যাশা করছিলাম ... সম্ভবত এটি সত্য যে আপনি সম্ভাব্যতর দ্রুততর এসটিডি :: সি ++ সমাধানের ধরণের পরিবর্তে qsort ব্যবহার করছেন?
স্থির_আরত্তি

1

মাইকেল হার্ফ ( রেডিক্স ট্রিকস ) দ্বারা রেডিক্স সাজানোর বাস্তবায়নটি দেখুন । std::sortআমার প্রথম উত্তরের অ্যালগরিদমের তুলনায় আমার মেশিনে বাছাই করা 5 গুণ দ্রুত ছিল । বাছাই করা ফাংশনের নাম RadixSort11

int main(void)
{
    std::ifstream ifs("C:\\Temp\\gaussian.dat", std::ios::binary | std::ios::in);
    std::vector<float> v;
    v.reserve(50000000);
    double d;
    while (ifs.read(reinterpret_cast<char*>(&d), sizeof(double)))
        v.push_back(static_cast<float>(d));
    std::vector<float> vres(v.size(), 0.0);
    clock_t c0 = clock();
    RadixSort11(&v[0], &vres[0], v.size());
    std::cout << "Finished after: "
              << static_cast<double>(clock() - c0) / CLOCKS_PER_SEC << std::endl;
    return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.