10 নম্বর বাছাই করার দ্রুততম উপায়? (সংখ্যা 32 বিট)


211

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

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

কীভাবে এই সমস্যার কাছে যাওয়া যায় সে সম্পর্কে কারও কি ধারণা আছে?


14
এটি যতটা অপরিশোধিত শোনাচ্ছে, নেস্টেড ifবিবৃতিগুলির একটি সিরিজ সেরা কাজ করা উচিত। লুপগুলি এড়িয়ে চলুন।
জন আলেক্সিউ

8
আপনি কি আশা করেন যে নম্বরগুলি আদেশের সেটগুলিতে কোনও পক্ষপাত সহ আপনাকে দেওয়া হবে, বা সেগুলি সমানভাবে বিতরণ করা হবে? একটি তালিকার অর্ডার এবং পরবর্তী তালিকার মধ্যে কি কোনও সম্পর্ক থাকবে?
ডগলাস জারে

4
পুরো ডেটা সেটটি (বিলিয়ন সংখ্যা সহ) বেনফোর্ডের আইন অনুসারে বিতরণ করা হয়েছে তবে আমি যখন এই সেট থেকে এলোমেলোভাবে উপাদানগুলি বেছে নেব, সেগুলি আর থাকবে না (আমার ধারণা)।
বোডাসিদো

13
আপনি এই পড়া করতে পারেন stackoverflow.com/q/2786899/995714
phuclv

11
যদি আপনি কোটি কোটি উপাদান থেকে এলোমেলোভাবে নির্বাচন করে থাকেন তবে পুরো ডেটা সেটটি র‌্যামে থাকা সত্ত্বেও নির্বাচিত উপাদানগুলিকে বাছাই করা সময়ের চেয়ে তথ্যের মধ্যে তথ্যটি টান দেওয়ার ক্ষেত্রে যে প্রভাবশালী হতে পারে তা বেশ কার্যকর হতে পারে। আপনি এলোমেলোভাবে ক্রমানুসারে ডেটা নির্বাচন করে বেঞ্চমার্কিং পারফরম্যান্সের মাধ্যমে প্রভাবটি পরীক্ষা করতে পারেন।
স্টিভ এস

উত্তর:


213

(বাছাই করা নেটওয়ার্কগুলি সন্ধান করার জন্য হ্যালোওয়ার্ল্ডের পরামর্শ অনুসরণ করে।)

দেখে মনে হচ্ছে একটি 29-তুলনা / সোয়াপ নেটওয়ার্ক 10-ইনপুট বাছাই করার দ্রুততম উপায়। আমি ১৯69৯ সালে ওয়াকসম্যানের দ্বারা আবিষ্কৃত নেটওয়ার্কটি জাভাস্ক্রিপ্টে এই উদাহরণের জন্য ব্যবহার করেছি, যা সরাসরি সিতে অনুবাদ করা উচিত, কারণ এটি কেবল ifবিবৃতি, তুলনা এবং অদলবদলের একটি তালিকা ।

function sortNet10(data) {	// ten-input sorting network by Waksman, 1969
    var swap;
    if (data[0] > data[5]) { swap = data[0]; data[0] = data[5]; data[5] = swap; }
    if (data[1] > data[6]) { swap = data[1]; data[1] = data[6]; data[6] = swap; }
    if (data[2] > data[7]) { swap = data[2]; data[2] = data[7]; data[7] = swap; }
    if (data[3] > data[8]) { swap = data[3]; data[3] = data[8]; data[8] = swap; }
    if (data[4] > data[9]) { swap = data[4]; data[4] = data[9]; data[9] = swap; }
    if (data[0] > data[3]) { swap = data[0]; data[0] = data[3]; data[3] = swap; }
    if (data[5] > data[8]) { swap = data[5]; data[5] = data[8]; data[8] = swap; }
    if (data[1] > data[4]) { swap = data[1]; data[1] = data[4]; data[4] = swap; }
    if (data[6] > data[9]) { swap = data[6]; data[6] = data[9]; data[9] = swap; }
    if (data[0] > data[2]) { swap = data[0]; data[0] = data[2]; data[2] = swap; }
    if (data[3] > data[6]) { swap = data[3]; data[3] = data[6]; data[6] = swap; }
    if (data[7] > data[9]) { swap = data[7]; data[7] = data[9]; data[9] = swap; }
    if (data[0] > data[1]) { swap = data[0]; data[0] = data[1]; data[1] = swap; }
    if (data[2] > data[4]) { swap = data[2]; data[2] = data[4]; data[4] = swap; }
    if (data[5] > data[7]) { swap = data[5]; data[5] = data[7]; data[7] = swap; }
    if (data[8] > data[9]) { swap = data[8]; data[8] = data[9]; data[9] = swap; }
    if (data[1] > data[2]) { swap = data[1]; data[1] = data[2]; data[2] = swap; }
    if (data[3] > data[5]) { swap = data[3]; data[3] = data[5]; data[5] = swap; }
    if (data[4] > data[6]) { swap = data[4]; data[4] = data[6]; data[6] = swap; }
    if (data[7] > data[8]) { swap = data[7]; data[7] = data[8]; data[8] = swap; }
    if (data[1] > data[3]) { swap = data[1]; data[1] = data[3]; data[3] = swap; }
    if (data[4] > data[7]) { swap = data[4]; data[4] = data[7]; data[7] = swap; }
    if (data[2] > data[5]) { swap = data[2]; data[2] = data[5]; data[5] = swap; }
    if (data[6] > data[8]) { swap = data[6]; data[6] = data[8]; data[8] = swap; }
    if (data[2] > data[3]) { swap = data[2]; data[2] = data[3]; data[3] = swap; }
    if (data[4] > data[5]) { swap = data[4]; data[4] = data[5]; data[5] = swap; }
    if (data[6] > data[7]) { swap = data[6]; data[6] = data[7]; data[7] = swap; }
    if (data[3] > data[4]) { swap = data[3]; data[3] = data[4]; data[4] = swap; }
    if (data[5] > data[6]) { swap = data[5]; data[5] = data[6]; data[6] = swap; }
    return(data);
}

alert(sortNet10([5,7,1,8,4,3,6,9,2,0]));

এখানে নেটওয়ার্কের একটি গ্রাফিকাল উপস্থাপনা, স্বতন্ত্র পর্যায়ে বিভক্ত। সমান্তরাল প্রক্রিয়াকরণের সুবিধা গ্রহণের জন্য, 5-4-3-4-4-4-4-3-2-2 গোষ্ঠীকরণ একটি 4-4-4-4-4-4-4-3-3-2 গ্রুপিংয়ে পরিবর্তন করা যেতে পারে।
10-ইনপুট বাছাই নেটওয়ার্ক (ওয়াক্সম্যান, 1969)

10-ইনপুট সাজানোর নেটওয়ার্ক (ওয়াক্সম্যান, 1969) পুনরায় গ্রুপ করা হয়েছে


69
পরামর্শ; একটি অদলবদল ম্যাক্রো ব্যবহার করুন। পছন্দ#define SORTPAIR(data, i1, i2) if (data[i1] > data[i2]) { int swap = data[i1]... }
পিটার কর্ডেস

9
এটি কি সর্বনিম্ন তা যুক্তিযুক্তভাবে দেখানো যেতে পারে?
corsiKa

8
@ করসিকা হ্যাঁ, কম্পিউটার বিজ্ঞানের প্রথম দিক থেকেই বাছাই করা নেটওয়ার্কগুলি গবেষণার একটি ক্ষেত্র ছিল। অনেক ক্ষেত্রেই সর্বোত্তম সমাধানগুলি কয়েক দশক ধরে পরিচিত। দেখুন en.wikipedia.org/wiki/Sorting_network
m69 '' snarky এবং unwelcoming ''

8
আমি পরীক্ষার জন্য একটি জাস্পারফ তৈরি করেছি এবং আমি নিশ্চিত করতে পারি যে ব্রাউজারগুলির নেটিভ সাজানোর কারণে নেটওয়ার্ক বাছাই 20 গুণ বেশি দ্রুত। jsperf.com/fastest-10-number-sort
ড্যানিয়েল

9
@ কেটাই এটি আপনার সংকলক উত্পাদন করতে পারে এমন কোনও অপ্টিমাইজেশন নষ্ট করবে। খারাপ ধারণা। আরও তথ্য এই পড়ুন en.wikipedia.org/wiki/...
Antzi

88

আপনি যখন এই নির্দিষ্ট আকারটির সাথে ডিল করেন তখন বাছাই করা নেটওয়ার্কগুলি একবার দেখুন । এই অ্যালগরিদমগুলির একটি নির্দিষ্ট রানটাইম থাকে এবং তাদের ইনপুটটিতে স্বতন্ত্র। আপনার ব্যবহারের ক্ষেত্রে আপনার কাছে এমন ওভারহেড নেই যা কিছু বাছাই করা অ্যালগরিদমগুলিতে থাকে।

বিটোনিক সাজান এই জাতীয় নেটওয়ার্কের একটি বাস্তবায়ন। এটি একটি সিপিইউতে লেন (এন) <= 32 দিয়ে সেরা কাজ করে। বড় ইনপুটগুলিতে আপনি জিপিইউতে যাওয়ার কথা ভাবতে পারেন। https://en.wikipedia.org/wiki/Sorting_network

Btw, বাছাই করা অ্যালগরিদমগুলির তুলনা করার জন্য একটি ভাল পৃষ্ঠা এটি এখানে রয়েছে (যদিও এটি অনুপস্থিত bitonic sort

http://www.sorting-algorithms.com


3
@ এরিকজি.হ্যাগস্ট্রোম অনেকগুলি সমাধান রয়েছে; যতক্ষণ না তারা 29 টি তুলনা ব্যবহার করে ততক্ষণ তারা সমান দক্ষ। আমি 1969 সাল থেকে ওয়াক্সম্যানের সমাধানটি ব্যবহার করেছি; সম্ভবত তিনি 29-তুলনা সংস্করণ আবিষ্কার করেছিলেন।
এম 69 '' ছদ্মবেশী এবং অপ্রত্যাশিত ''

1
হ্যাঁ, @ এম 69। এক মিলিয়নেরও বেশি রয়েছে। ওয়াক্সম্যানের দ্রবণটির দৈর্ঘ্য 29 এবং গভীরতা 9 has
এরিক জি। হ্যাগ্রস্টোম

4
@ এরিকজি.হ্যাগস্ট্রোম স্পষ্টতই 7 গভীরতার সাথে 87 টি সমাধান রয়েছে, এর মধ্যে প্রথমটি 1973 সালে নথের দ্বারা পাওয়া গেছে, তবে আমি একটি দ্রুত গুগলের সাথে তাদের কোনও সন্ধান করতে সক্ষম হইনি। larc.unt.edu/ian/pubs/9-input.pdf (উপসংহার দেখুন, p.14 দেখুন)
এম 69 '' ছদ্মবেশী এবং অপ্রত্যাশিত ''

4
@ এরিকজি.হ্যাগস্ট্রোম: গভীরতা "সি স্তরে" কোনও তাত্পর্যপূর্ণ হতে পারে না, তবে সম্ভবত সংকলক এবং সিপিইউ এর সাথে সমাপ্ত হলে, কিছুটা সম্ভাবনা রয়েছে যে এটি আংশিকভাবে সিপিইউতে সমান্তরাল হবে এবং তাই ছোট গভীরতা সাহায্য করতে পারে। সিপিইউর উপর নির্ভর করে অবশ্যই: কিছু সিপিইউ তুলনামূলকভাবে সহজ এবং একের পর এক কাজ করে, কিছু সিপিইউতে ফ্লাইটে একাধিক অপারেশন থাকতে পারে, বিশেষত আপনি যে কোনও স্ট্রয়ে প্রয়োজনীয় স্ট্যাকের জন্য খুব বেশি পারফরম্যান্স পেতে পারেন might 10 টি ভেরিয়েবলগুলি কীভাবে সম্পন্ন হয়েছে তার উপর নির্ভর করে ম্যানিপুলেট করার আদেশ দিন।
স্টিভ জেসোপ

1
@ এরিকজি.হ্যাগস্ট্রোম ইয়ান পারবেরির দ্বারা কাগজটি তাৎক্ষণিকভাবে পরিষ্কার করা যায় নি, তবে গভীরতা-networks নেটওয়ার্কগুলির দৈর্ঘ্য ২৯-এরও বেশি রয়েছে। নথ, "আর্ট অফ কম্পিউটার প্রোগ্রামিং ভলিউমআইআইআই", §5.3.4, ডুমুর দেখুন । 49 এবং 51.
m69 '' ছদ্মবেশী এবং অপ্রত্যাশিত ''

33

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

x86 এসএসইতে 32 টি 32 বিট ইন্টের ভেক্টরগুলির জন্য প্যাকড -32 বিট-ইন্টিজার ন্যূনতম এবং সর্বাধিক নির্দেশনা রয়েছে। এভিএক্স 2 (হাসওয়েল এবং তার পরে) এর সাথে একই তবে 8 ইন্টের 256 বি ভেক্টর রয়েছে। দক্ষ সাফ করার নির্দেশাবলীও রয়েছে।

আপনার যদি স্বতন্ত্র ছোট আকারের অনেকগুলি থাকে তবে ভেক্টরগুলি ব্যবহার করে সমান্তরালে 4 বা 8 ধরণের করা সম্ভব হতে পারে। ESP। যদি আপনি এলোমেলোভাবে উপাদানগুলি বেছে নিচ্ছেন (যাতে সাজানো ডেটা কোনওভাবেই মেমরির সাথে স্বাদযুক্ত হবে না), আপনি শ্যাফেলগুলি এড়াতে এবং আপনার প্রয়োজনীয় ক্রমের সাথে তুলনা করতে পারেন। 4 (এভিএক্স 2: 8) থেকে 10 টি ইনটগুলির তালিকা থেকে সমস্ত ডেটা ধরে রাখতে 10 টি রেজিস্টার এখনও স্ক্র্যাচ স্পেসের জন্য 6 টি রেজি রেখে দেয়।

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


26

একটি অনিবন্ধিত, শাখা-কম নির্বাচনের ধরণের সম্পর্কে কী?

#include <iostream>
#include <algorithm>
#include <random>

//return the index of the minimum element in array a
int min(const int * const a) {
  int m = a[0];
  int indx = 0;
  #define TEST(i) (m > a[i]) && (m = a[i], indx = i ); 
  //see http://stackoverflow.com/a/7074042/2140449
  TEST(1);
  TEST(2);
  TEST(3);
  TEST(4);
  TEST(5);
  TEST(6);
  TEST(7);
  TEST(8);
  TEST(9);
  #undef TEST
  return indx;
}

void sort( int * const a ){
  int work[10];
  int indx;
  #define GET(i) indx = min(a); work[i] = a[indx]; a[indx] = 2147483647; 
  //get the minimum, copy it to work and set it at max_int in a
  GET(0);
  GET(1);
  GET(2);
  GET(3);
  GET(4);
  GET(5);
  GET(6);
  GET(7);
  GET(8);
  GET(9);
  #undef GET
  #define COPY(i) a[i] = work[i];
  //copy back to a
  COPY(0);
  COPY(1);
  COPY(2);
  COPY(3);
  COPY(4);
  COPY(5);
  COPY(6);
  COPY(7);
  COPY(8);
  COPY(9);
  #undef COPY
}

int main() {
  //generating and printing a random array
  int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
  std::random_device rd;
  std::mt19937 g(rd());
  std::shuffle( a, a+10, g);
  for (int i = 0; i < 10; i++) {
    std::cout << a[i] << ' ';
  }
  std::cout << std::endl;

  //sorting and printing again
  sort(a);
  for (int i = 0; i < 10; i++) {
    std::cout << a[i] << ' ';
  } 

  return 0;
}

http://coliru.stacked-crooked.com/a/71e18bc4f7fa18c6

কেবলমাত্র প্রাসঙ্গিক লাইনগুলি প্রথম দুটি #define

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


মাপকাঠি

আমি বাছাই করা নেটওয়ার্কের বিরুদ্ধে বেঞ্চমার্ক করেছি এবং আমার কোডটি ধীর বলে মনে হচ্ছে। তবে আমি অনিরোলিং এবং অনুলিপিটি সরিয়ে দেওয়ার চেষ্টা করেছি। এই কোড চলছে:

#include <iostream>
#include <algorithm>
#include <random>
#include <chrono>

int min(const int * const a, int i) {
  int m = a[i];
  int indx = i++;
  for ( ; i<10; i++) 
    //see http://stackoverflow.com/a/7074042/2140449
    (m > a[i]) && (m = a[i], indx = i ); 
  return indx;
}

void sort( int * const a ){
  for (int i = 0; i<9; i++)
    std::swap(a[i], a[min(a,i)]); //search only forward
}


void sortNet10(int * const data) {  // ten-input sorting network by Waksman, 1969
    int swap;
    if (data[0] > data[5]) { swap = data[0]; data[0] = data[5]; data[5] = swap; }
    if (data[1] > data[6]) { swap = data[1]; data[1] = data[6]; data[6] = swap; }
    if (data[2] > data[7]) { swap = data[2]; data[2] = data[7]; data[7] = swap; }
    if (data[3] > data[8]) { swap = data[3]; data[3] = data[8]; data[8] = swap; }
    if (data[4] > data[9]) { swap = data[4]; data[4] = data[9]; data[9] = swap; }
    if (data[0] > data[3]) { swap = data[0]; data[0] = data[3]; data[3] = swap; }
    if (data[5] > data[8]) { swap = data[5]; data[5] = data[8]; data[8] = swap; }
    if (data[1] > data[4]) { swap = data[1]; data[1] = data[4]; data[4] = swap; }
    if (data[6] > data[9]) { swap = data[6]; data[6] = data[9]; data[9] = swap; }
    if (data[0] > data[2]) { swap = data[0]; data[0] = data[2]; data[2] = swap; }
    if (data[3] > data[6]) { swap = data[3]; data[3] = data[6]; data[6] = swap; }
    if (data[7] > data[9]) { swap = data[7]; data[7] = data[9]; data[9] = swap; }
    if (data[0] > data[1]) { swap = data[0]; data[0] = data[1]; data[1] = swap; }
    if (data[2] > data[4]) { swap = data[2]; data[2] = data[4]; data[4] = swap; }
    if (data[5] > data[7]) { swap = data[5]; data[5] = data[7]; data[7] = swap; }
    if (data[8] > data[9]) { swap = data[8]; data[8] = data[9]; data[9] = swap; }
    if (data[1] > data[2]) { swap = data[1]; data[1] = data[2]; data[2] = swap; }
    if (data[3] > data[5]) { swap = data[3]; data[3] = data[5]; data[5] = swap; }
    if (data[4] > data[6]) { swap = data[4]; data[4] = data[6]; data[6] = swap; }
    if (data[7] > data[8]) { swap = data[7]; data[7] = data[8]; data[8] = swap; }
    if (data[1] > data[3]) { swap = data[1]; data[1] = data[3]; data[3] = swap; }
    if (data[4] > data[7]) { swap = data[4]; data[4] = data[7]; data[7] = swap; }
    if (data[2] > data[5]) { swap = data[2]; data[2] = data[5]; data[5] = swap; }
    if (data[6] > data[8]) { swap = data[6]; data[6] = data[8]; data[8] = swap; }
    if (data[2] > data[3]) { swap = data[2]; data[2] = data[3]; data[3] = swap; }
    if (data[4] > data[5]) { swap = data[4]; data[4] = data[5]; data[5] = swap; }
    if (data[6] > data[7]) { swap = data[6]; data[6] = data[7]; data[7] = swap; }
    if (data[3] > data[4]) { swap = data[3]; data[3] = data[4]; data[4] = swap; }
    if (data[5] > data[6]) { swap = data[5]; data[5] = data[6]; data[6] = swap; }
}


std::chrono::duration<double> benchmark( void(*func)(int * const), const int seed ) {
  std::mt19937 g(seed);
  int a[10] = {10,11,12,13,14,15,16,17,18,19};
  std::chrono::high_resolution_clock::time_point t1, t2; 
  t1 = std::chrono::high_resolution_clock::now();
  for (long i = 0; i < 1e7; i++) {
    std::shuffle( a, a+10, g);
    func(a);
  }
  t2 = std::chrono::high_resolution_clock::now();
  return std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
}

int main() {
  std::random_device rd;
  for (int i = 0; i < 10; i++) {
    const int seed = rd();
    std::cout << "seed = " << seed << std::endl;
    std::cout << "sortNet10: " << benchmark(sortNet10, seed).count() << std::endl;
    std::cout << "sort:      " << benchmark(sort,      seed).count() << std::endl;
  }
  return 0;
}

আমি ক্রমানুসারে বাছাই করা নেটওয়ার্কের তুলনায় ব্রাঞ্চ-কম সিলেক্টেশনের জন্য ধারাবাহিকভাবে ভাল ফলাফল পাচ্ছি ।

$ gcc -v
gcc version 5.2.0 (GCC) 
$ g++ -std=c++11 -Ofast sort.cpp && ./a.out
seed = -1727396418
sortNet10: 2.24137
sort:      2.21828
seed = 2003959850
sortNet10: 2.23914
sort:      2.21641
seed = 1994540383
sortNet10: 2.23782
sort:      2.21778
seed = 1258259982
sortNet10: 2.25199
sort:      2.21801
seed = 1821086932
sortNet10: 2.25535
sort:      2.2173
seed = 412262735
sortNet10: 2.24489
sort:      2.21776
seed = 1059795817
sortNet10: 2.29226
sort:      2.21777
seed = -188551272
sortNet10: 2.23803
sort:      2.22996
seed = 1043757247
sortNet10: 2.2503
sort:      2.23604
seed = -268332483
sortNet10: 2.24455
sort:      2.24304

4
ফলাফলগুলি খুব চিত্তাকর্ষক নয়, তবে আসলে যা আমি প্রত্যাশা করতাম। বাছাইয়ের নেটওয়ার্কটি অদলবদল নয়, তুলনা হ্রাস করে। যখন সমস্ত মান ইতিমধ্যে ক্যাশে থাকে তখন অদলবদলের তুলনায় অনেক কম সস্তা হয়, সুতরাং একটি নির্বাচনের ধরণের (যেটি অদলবদলের সংখ্যাকে হ্রাস করে) উপরের দিকে থাকে। (এবং আরও অনেক তুলনা নেই: 29 টি মিশ্রণ সহ 29 টি সোয়াপ পর্যন্ত নেটওয়ার্ক? বনাম 45 টি তুলনা এবং সর্বাধিক 9 স্বাপের সাথে বাছাই বাছাই)
উদাহরণস্বরূপ

7
ওহ এবং এর শাখা রয়েছে - যদি না লাইনটি for ( ; i<10; i++) (m > a[i]) && (m = a[i], indx = i ); খুব ভালভাবে অনুকূল হয়। (শর্ট সার্কিট করা সাধারণত শাখা প্রশাখার একটি রূপ)
উদাহরণস্বরূপ,

1
@ ইউজিনিআরবটসেভ এটিও, তবে এটি সর্বদা একই র্যান্ডম ক্রম দিয়ে খাওয়ানো হয় যাতে এটি বাতিল করা উচিত should আমি পরিবর্তন করার চেষ্টা করেছিল std::shuffleসঙ্গে for (int n = 0; n<10; n++) a[n]=g();। কার্যকর করার সময় অর্ধেক হয়ে গেছে এবং নেটওয়ার্ক এখন দ্রুত is
দারিওপি

এটি libc ++ এর সাথে কীভাবে তুলনা করবে std::sort?
gnzlbg

1
@gnzlbg আমিও চেষ্টা std::sortকরেছি কিন্তু এটি এত খারাপভাবে পারফর্ম করছিল যে আমি এটিকে মানদণ্ডে অন্তর্ভুক্তও করি নি। আমি অনুমান করি যে ক্ষুদ্র ডাটা সেটগুলির সাথে যথেষ্ট ওভারহেড রয়েছে।
দারিওপ

20

প্রশ্নটি বলে না যে এটি কোনও ধরণের ওয়েব-ভিত্তিক অ্যাপ্লিকেশন। আমার দৃষ্টি আকর্ষণীয় একটি জিনিস ছিল:

আমি কোটি কোটি উপাদানগুলির একটি ডেটা সেট নমুনা দিচ্ছি এবং প্রতিবারই এর থেকে 10 সংখ্যা বাছাই করতে হবে (সরলীকৃত) এবং তাদের বাছাই করতে হবে (এবং সাজানো 10 উপাদান তালিকার থেকে সিদ্ধান্তে নেওয়া)।

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

কিছু স্তরে একমাত্র সীমাবদ্ধ ফ্যাক্টরটি হয়ে যায় আপনি কত দ্রুত এফপিজিএতে ডেটা বেলতে পারবেন এবং আপনি কীভাবে তা বের করতে পারবেন।

রেফারেন্সের পয়েন্ট হিসাবে, আমি একটি উচ্চ কার্যকারিতা রিয়েল-টাইম চিত্র প্রসেসর ডিজাইন করেছি যা প্রতি সেকেন্ডে প্রায় 300 মিলিয়ন পিক্সেল হারে 32 বিট আরজিবি চিত্র ডেটা পেয়েছে। অন্য প্রান্তটি আসার আগে তথ্যগুলি এফআইআর ফিল্টার, ম্যাট্রিক্স মাল্টিপ্লায়ারস, লুউকিং টেবিল, স্থানীয় প্রান্ত সনাক্তকরণ ব্লক এবং অন্যান্য ক্রিয়াকলাপের মাধ্যমে প্রবাহিত হয়েছিল। অভ্যন্তরীণ ক্লকিং প্রায় 33MHz থেকে বিস্তৃত একটি অপেক্ষাকৃত ছোট Xilinx Virtex2 FPGA- এ, যদি আমি সঠিকভাবে মনে করি, 400MHz। ওহ, হ্যাঁ, এটির একটি ডিডিআর 2 কন্ট্রোলার বাস্তবায়নও ছিল এবং এটি ডিডিআর 2 মেমরির দুটি ব্যাঙ্ক চালিয়েছিল।

একটি এফপিজিএ শত শত মেগাহার্টজ চলাকালীন প্রতিটি ক্লক ট্রানজিশনে দশ 32 বিট সংখ্যার আউটপুট দিতে পারে can ডেটা প্রসেসিং পাইপলাইন / গুলি পূরণ করার কারণে ক্রিয়াকলাপের শুরুতে অল্প বিলম্ব হবে। এর পরে আপনি প্রতি ঘন্টার জন্য একটি ফলাফল পেতে সক্ষম হবেন। বা আরও অনেকগুলি যদি প্রক্রিয়াকরণটিকে বাছাইয়ের মাধ্যমে বিশ্লেষণ পাইপলাইনের মাধ্যমে সমান্তরাল করা যায়। নীতিগতভাবে সমাধানটি প্রায় তুচ্ছ।

কথাটি হ'ল: যদি অ্যাপ্লিকেশনটি পিসি-আবদ্ধ না হয় এবং ডেটা স্ট্রিম এবং প্রসেসিং কোনও এফপিজিএ সমাধানের সাথে "সামঞ্জস্যপূর্ণ" হয় (হয় একা থাকে বা মেশিনে কো-প্রসেসর কার্ড হিসাবে) আপনার যাওয়ার কোনও উপায় নেই অ্যালগরিদম নির্বিশেষে যে কোনও ভাষায় লিখিত সফ্টওয়্যার দিয়ে সম্পাদনযোগ্য স্তরের পারফরম্যান্সকে পরাজিত করতে সক্ষম হতে।

সম্পাদনা করুন:

সবেমাত্র তাত্ক্ষণিক অনুসন্ধান চালিয়েছেন এবং এমন একটি কাগজ পেয়েছেন যা আপনার পক্ষে কার্যকর হতে পারে। দেখে মনে হচ্ছে এটি ২০১২ সালের মত। আপনি আজ পারফরম্যান্সে আরও অনেক কিছু করতে পারেন (এবং তারপরেও ফিরে)। এটা এখানে:

এফপিজিএগুলিতে নেটওয়ার্ক বাছাই করা হচ্ছে


10

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

এটি 10 ​​সংখ্যার জন্য খুব দ্রুত বাছাই করতে ব্যবহৃত হতে পারে।

/**
 * A Functor class to create a sort for fixed sized arrays/containers with a
 * compile time generated Bose-Nelson sorting network.
 * \tparam NumElements  The number of elements in the array or container to sort.
 * \tparam T            The element type.
 * \tparam Compare      A comparator functor class that returns true if lhs < rhs.
 */
template <unsigned NumElements, class Compare = void> class StaticSort
{
    template <class A, class C> struct Swap
    {
        template <class T> inline void s(T &v0, T &v1)
        {
            T t = Compare()(v0, v1) ? v0 : v1; // Min
            v1 = Compare()(v0, v1) ? v1 : v0; // Max
            v0 = t;
        }

        inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); }
    };

    template <class A> struct Swap <A, void>
    {
        template <class T> inline void s(T &v0, T &v1)
        {
            // Explicitly code out the Min and Max to nudge the compiler
            // to generate branchless code.
            T t = v0 < v1 ? v0 : v1; // Min
            v1 = v0 < v1 ? v1 : v0; // Max
            v0 = t;
        }

        inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); }
    };

    template <class A, class C, int I, int J, int X, int Y> struct PB
    {
        inline PB(A &a)
        {
            enum { L = X >> 1, M = (X & 1 ? Y : Y + 1) >> 1, IAddL = I + L, XSubL = X - L };
            PB<A, C, I, J, L, M> p0(a);
            PB<A, C, IAddL, J + M, XSubL, Y - M> p1(a);
            PB<A, C, IAddL, J, XSubL, M> p2(a);
        }
    };

    template <class A, class C, int I, int J> struct PB <A, C, I, J, 1, 1>
    {
        inline PB(A &a) { Swap<A, C> s(a, I - 1, J - 1); }
    };

    template <class A, class C, int I, int J> struct PB <A, C, I, J, 1, 2>
    {
        inline PB(A &a) { Swap<A, C> s0(a, I - 1, J); Swap<A, C> s1(a, I - 1, J - 1); }
    };

    template <class A, class C, int I, int J> struct PB <A, C, I, J, 2, 1>
    {
        inline PB(A &a) { Swap<A, C> s0(a, I - 1, J - 1); Swap<A, C> s1(a, I, J - 1); }
    };

    template <class A, class C, int I, int M, bool Stop = false> struct PS
    {
        inline PS(A &a)
        {
            enum { L = M >> 1, IAddL = I + L, MSubL = M - L};
            PS<A, C, I, L, (L <= 1)> ps0(a);
            PS<A, C, IAddL, MSubL, (MSubL <= 1)> ps1(a);
            PB<A, C, I, IAddL, L, MSubL> pb(a);
        }
    };

    template <class A, class C, int I, int M> struct PS <A, C, I, M, true>
    {
        inline PS(A &a) {}
    };

public:
    /**
     * Sorts the array/container arr.
     * \param  arr  The array/container to be sorted.
     */
    template <class Container> inline void operator() (Container &arr) const
    {
        PS<Container, Compare, 1, NumElements, (NumElements <= 1)> ps(arr);
    };

    /**
     * Sorts the array arr.
     * \param  arr  The array to be sorted.
     */
    template <class T> inline void operator() (T *arr) const
    {
        PS<T*, Compare, 1, NumElements, (NumElements <= 1)> ps(arr);
    };
};

#include <iostream>
#include <vector>

int main(int argc, const char * argv[])
{
    enum { NumValues = 10 };

    // Arrays
    {
        int rands[NumValues];
        for (int i = 0; i < NumValues; ++i) rands[i] = rand() % 100;
        std::cout << "Before Sort: \t";
        for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
        std::cout << "\n";
        StaticSort<NumValues> staticSort;
        staticSort(rands);
        std::cout << "After Sort: \t";
        for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
        std::cout << "\n";
    }

    std::cout << "\n";

    // STL Vector
    {
        std::vector<int> rands(NumValues);
        for (int i = 0; i < NumValues; ++i) rands[i] = rand() % 100;
        std::cout << "Before Sort: \t";
        for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
        std::cout << "\n";
        StaticSort<NumValues> staticSort;
        staticSort(rands);
        std::cout << "After Sort: \t";
        for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
        std::cout << "\n";
    }

    return 0;
}

মনে রাখবেন যে একটি if (compare) swapবিবৃতি পরিবর্তে , আমরা স্পষ্টভাবে ন্যূনতম এবং সর্বাধিক জন্য টার্নারি অপারেটরদের কোড আউট করি। এটি শাখাবিহীন কোড ব্যবহার করে সংকলককে টোকা দিতে সহায়তা করে।

benchmarks

নিম্নলিখিত মানদণ্ডগুলি ঝনঝন -৩৩ এর সাথে সংকলিত হয়ে আমার ২০১২ সালের মাঝামাঝি ম্যাকবুক এয়ারে চলেছে।

এলোমেলো তথ্য বাছাই করা হচ্ছে

দারিওপির কোডের সাথে এটির তুলনা করে, 10 মাপের 1 মিলিয়ন 32-বিট ইন্টি অ্যারেগুলি সাজানোর জন্য এখানে মিলি সেকেন্ডের সংখ্যা রয়েছে:

হার্ডকোডযুক্ত বাছাই নেট 10: 88.774 এমএস
বোস-নেলসন সাজান 10: 27.815 এমএস

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

বিভিন্ন আকারের 1 মিলিয়ন অ্যারে বাছাই করতে সময় (মিলিসেকেন্ডে)।
আকার 2, 4, 8 এর অ্যারেগুলির জন্য মিলিসেকেন্ডগুলির সংখ্যা যথাক্রমে 1.943, 8.655, 20.246।
সি ++ টেম্প্লেটেড বোস-নেলসন স্ট্যাটিক বাছাইয়ের সময়

ক্রেডিট নিবন্ধিত সন্নিবেশ সাজানোর জন্য গ্লেন টিটেলবাউমে

এখানে 6 টি উপাদানের ছোট অ্যারেগুলির জন্য সাজানোর জন্য গড় ঘড়ি রয়েছে। এই প্রশ্নটিতে বেঞ্চমার্ক কোড এবং উদাহরণগুলি পাওয়া যাবে:
নির্দিষ্টতম দৈর্ঘ্যের 6 ইনট অ্যারের দ্রুততম ধরণের

Direct call to qsort library function       : 326.81
Naive implementation (insertion sort)       : 132.98
Insertion Sort (Daniel Stutzbach)           : 104.04
Insertion Sort Unrolled                     : 99.64
Insertion Sort Unrolled (Glenn Teitelbaum)  : 81.55
Rank Order                                  : 44.01
Rank Order with registers                   : 42.40
Sorting Networks (Daniel Stutzbach)         : 88.06
Sorting Networks (Paul R)                   : 31.64
Sorting Networks 12 with Fast Swap          : 29.68
Sorting Networks 12 reordered Swap          : 28.61
Reordered Sorting Network w/ fast swap      : 24.63
Templated Sorting Network (this class)      : 25.37

এটি 6 টি উপাদানের জন্য প্রশ্নের দ্রুততম উদাহরণ হিসাবে তত দ্রুত সম্পাদন করে।

বাছাই করা ডেটা বাছাইয়ের জন্য পারফরম্যান্স

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

এখানে চিত্র বর্ণনা লিখুন

আপনি ডেটা উপর নির্ভর করে একটি উপযুক্ত বাছাই অ্যালগরিদম চয়ন করতে চাইতে পারেন।

মানদণ্ডগুলির জন্য ব্যবহৃত কোডটি এখানে পাওয়া যাবে


কোন সুযোগ আপনি নীচে আমার আলগো জন্য একটি তুলনা যোগ করতে পারেন?
গ্লেন টিটেলবাম

@ গ্লেনটিটেলবাম কোনও সুযোগ আপনি নিজের মানদণ্ডে প্রকাশ করেছেন এবং উপায় এবং ফলাফল প্রকাশ করেছেন?
গ্রেইবার্ড

বাছাই করা ইনপুটটিতে ডেটা যুক্ত করার জন্য কুদোস os
গ্রেইবার্ড

কয়েকটি সিস্টেমে v1 = v0 < v1 ? v1 : v0; // Maxএখনও শাখায় বিভক্ত হতে পারে, সে ক্ষেত্রে এটা দিয়ে প্রতিস্থাপিত হতে পারে v1 += v0 - tযদি কারণ tহয় v0তারপর v1 + v0 -t == v1 + v0 - v0 == v1আর tহয় v1এবংv1 + v0 -t == v1 + v0 - v1 == v0
গ্লেন Teitelbaum

তিনটি সাধারণত আধুনিক সংকলকগুলির উপর maxssবা minssনির্দেশের মধ্যে সংকলিত হয় । তবে যেখানে এটি কাজ করে না, সেখানে অদলবদলের অন্যান্য উপায় ব্যবহার করা যেতে পারে। :)
25:25 'এ

5

যদিও ছোট ছোট অ্যারেগুলিতে কোনও নেটওয়ার্ক সাজানোর দ্রুত পরিবর্তন হওয়ার পক্ষে খুব ভাল প্রতিক্রিয়া রয়েছে, তবে কখনও কখনও সঠিকভাবে অনুকূলিত হলে আপনি সন্নিবেশ সাজানোর বীটকে সফল করতে পারেন না। উদাহরণস্বরূপ ব্যাচ 2 টি উপাদান সহ সন্নিবেশ করান:

{
    final int a=in[0]<in[1]?in[0]:in[1];
    final int b=in[0]<in[1]?in[1]:in[0];
    in[0]=a;
    in[1]=b;
}
for(int x=2;x<10;x+=2)
{
    final int a=in[x]<in[x+1]?in[x]:in[x+1];
    final int b=in[x]<in[x+1]?in[x+1]:in[x];
    int y= x-1;

    while(y>=0&&in[y]>b)
    {
        in[y+2]= in[y];
        --y;
    }
    in[y+2]=b;
    while(y>=0&&in[y]>a)
    {
        in[y+1]= in[y];
        --y;
    }
    in[y+1]=a;
}

আপনি কেন পুনরাবৃত্তি করবেন তা নিশ্চিত নন in[y+2]= in[y];, টাইপো?
গ্লেন টিটেলবাউম 25'16

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

3

আপনি সম্পূর্ণরূপে তালিকাভুক্তি করতে পারেন insertion sort

এটি আরও সহজ করার জন্য, পুনরাবৃত্তকারীগুলি templateকোনও ফাংশন ওভারহেড ছাড়াই ব্যবহার করা যেতে পারে। যেহেতু এটি ইতিমধ্যে একটি template, intতাই templateপ্যারামিটারও হতে পারে । এটি 10 ​​টি তুচ্ছ ব্যতীত কোডিং অ্যারে মাপগুলি তৈরি করে।

নোট int x[10]করুন কলটি বাছাই করার জন্য insert_sort<int, 9>::sort(x);যেহেতু ক্লাসটি শেষ আইটেমের সূচি ব্যবহার করে। এটি মোড়ানো হতে পারে, তবে এটি পড়তে আরও কোড হবে।

template <class T, int NUM>
class insert_sort;

template <class T>
class insert_sort<T,0>
// stop template recursion
// sorting 1 item is a no-op
{
public:
    static void place(T *x) {}
    static void sort(T * x) {}
};

template <class T, int NUM>
class insert_sort
// use template recursion to do insertion sort
// NUM is the index of the last item, eg. for x[10] call <9>
{
public:
    static void place(T *x)
    {
        T t1=x[NUM-1];
        T t2=x[NUM];
        if (t1 > t2)
        {
            x[NUM-1]=t2;
            x[NUM]=t1;
            insert_sort<T,NUM-1>::place(x);
        }
    }
    static void sort(T * x)
    {
        insert_sort<T,NUM-1>::sort(x); // sort everything before
        place(x);                    // put this item in
    }
};

আমার পরীক্ষায় এটি বাছাই করা নেটওয়ার্ক উদাহরণগুলির চেয়ে দ্রুত ছিল।


0

আমি এখানে বর্ণিত সাদৃশ্যযুক্ত কারণে , নিম্নলিখিত বাছাই করা ফাংশনগুলি sort6_iterator()এবং sort10_iterator_local()ভাল সম্পাদন করা উচিত, যেখানে বাছাইয়ের নেটওয়ার্কটি এখান থেকে নেওয়া হয়েছিল :

template<class IterType> 
inline void sort10_iterator(IterType it) 
{
#define SORT2(x,y) {if(data##x>data##y)std::swap(data##x,data##y);}
#define DD1(a)   auto data##a=*(data+a);
#define DD2(a,b) auto data##a=*(data+a), data##b=*(data+b);
#define CB1(a)   *(data+a)=data##a;
#define CB2(a,b) *(data+a)=data##a;*(data+b)=data##b;
  DD2(1,4) SORT2(1,4) DD2(7,8) SORT2(7,8) DD2(2,3) SORT2(2,3) DD2(5,6) SORT2(5,6) DD2(0,9) SORT2(0,9) 
  SORT2(2,5) SORT2(0,7) SORT2(8,9) SORT2(3,6) 
  SORT2(4,9) SORT2(0,1) 
  SORT2(0,2) CB1(0) SORT2(6,9) CB1(9) SORT2(3,5) SORT2(4,7) SORT2(1,8) 
  SORT2(3,4) SORT2(5,8) SORT2(6,7) SORT2(1,2) 
  SORT2(7,8) CB1(8) SORT2(1,3) CB1(1) SORT2(2,5) SORT2(4,6) 
  SORT2(2,3) CB1(2) SORT2(6,7) CB1(7) SORT2(4,5) 
  SORT2(3,4) CB2(3,4) SORT2(5,6) CB2(5,6) 
#undef CB1
#undef CB2
#undef DD1
#undef DD2
#undef SORT2
}

এই ফাংশনটি কল করতে আমি এটির std::vectorপুনরুক্তি দিয়েছি ।


0

একটি সন্নিবেশ সাজানোর জন্য 9 টির সবচেয়ে ভাল ক্ষেত্রে এবং 45 টির মধ্যে সবচেয়ে খারাপ (10 টি ইনপুট যা বিপরীত ক্রমে রয়েছে) দিয়ে 10 ইনপুটগুলি বাছাই করতে গড়ে 29,6 তুলনা প্রয়োজন।

একটি 9,6,1} শেলসোর্টের 10 ইনপুটগুলি বাছাই করতে গড়ে 25.5 তুলনা প্রয়োজন। সেরা কেসটি 14 তুলনা, সবচেয়ে খারাপ 34 এবং বিপরীত ইনপুটটি বাছাইয়ের জন্য 22 টি প্রয়োজন।

সুতরাং সন্নিবেশ বাছাইয়ের পরিবর্তে শেলসোর্ট ব্যবহার করে গড় কেস 14% কমে যায়। যদিও সেরা ক্ষেত্রে 56% বৃদ্ধি পেয়েছে সবচেয়ে খারাপ ক্ষেত্রে 24% হ্রাস পেয়েছে যা অ্যাপ্লিকেশনগুলিতে উল্লেখযোগ্য যেখানে সবচেয়ে খারাপ ক্ষেত্রে কর্মক্ষমতা চেক রাখা গুরুত্বপূর্ণ check বিপরীত কেসটি 51% হ্রাস পেয়েছে।

যেহেতু আপনি সন্নিবেশ সাজানোর সাথে পরিচিত বলে মনে হচ্ছে আপনি {9,6 for এর জন্য বাছাইকারী নেটওয়ার্ক হিসাবে অ্যালগরিদমটি প্রয়োগ করতে পারেন এবং তারপরে সন্নিবেশ সাজানোর ({1}) এ আলতো চাপুন:

i[0] with i[9]    // {9}

i[0] with i[6]    // {6}
i[1] with i[7]    // {6}
i[2] with i[8]    // {6}
i[3] with i[9]    // {6}

i[0 ... 9]        // insertion sort
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.