पूर्णांक हैश फ़ंक्शन क्या अच्छा है जो पूर्णांक हैश कुंजी स्वीकार करता है?


जवाबों:


47

नुथ की गुणन विधि:

hash(i)=i*2654435761 mod 2^32

सामान्य तौर पर, आपको एक गुणक चुनना चाहिए जो आपके हैश आकार ( 2^32उदाहरण में) के क्रम में है और इसके साथ कोई सामान्य कारक नहीं है। इस तरह से हैश फंक्शन आपके सभी हैश स्पेस को समान रूप से कवर करता है।

संपादित करें: इस हैश फ़ंक्शन का सबसे बड़ा नुकसान यह है कि यह विभाज्यता को संरक्षित करता है, इसलिए यदि आपके पूर्णांक 2 या 4 से विभाज्य हैं (जो असामान्य नहीं है), तो उनका हैश भी होगा। यह हैश तालिकाओं में एक समस्या है - आप उपयोग की जा रही बाल्टियों के केवल 1/2 या 1/4 के साथ समाप्त कर सकते हैं।


36
यह एक बहुत बुरा हैश फ़ंक्शन है, यद्यपि एक प्रसिद्ध नाम से जुड़ा हुआ है।
सीन ओसवा

5
यह एक बुरा हैश फ़ंक्शन नहीं है यदि प्राइम टेबल आकार के साथ उपयोग किया जाता है। इसके अलावा, यह बंद हैशिंग के लिए है। यदि हैश मान समान रूप से वितरित नहीं किए जाते हैं, तो गुणात्मक हैशिंग यह सुनिश्चित करता है कि एक मान से टकराव अन्य हैश मानों के साथ "परेशान" आइटम की संभावना नहीं है।
पाओलो बोन्जिनी

11
जिज्ञासु के लिए, यह स्थिरांक हैश के आकार (2 ^ 32) के लिए चुना जाता है, जो कि फी द्वारा विभाजित है
awdz9nld

7
पाओलो: नुथ का तरीका इस अर्थ में "बुरा" है कि यह ऊपरी बिट्स पर हिमस्खलन नहीं करता है
awdz9nld

9
करीब से निरीक्षण करने पर, यह पता चला कि 2654435761 वास्तव में एक प्रमुख संख्या है। तो शायद इसीलिए इसे 2654435769 के बजाय चुना गया था।
कराडोक

149

मैंने पाया कि निम्नलिखित एल्गोरिथ्म एक बहुत अच्छा सांख्यिकीय वितरण प्रदान करता है। प्रत्येक इनपुट बिट लगभग 50% प्रायिकता के साथ प्रत्येक आउटपुट बिट को प्रभावित करता है। कोई टक्कर नहीं है (प्रत्येक इनपुट एक अलग आउटपुट में परिणाम)। यदि सीपीयू में अंतर्निहित पूर्णांक गुणन इकाई नहीं है तो सिवाय एल्गोरिथ्म तेज है। सी कोड, संभालने के intलिए 32 बिट (जावा के लिए, के >>साथ बदलें >>>और निकालें unsigned):

unsigned int hash(unsigned int x) {
    x = ((x >> 16) ^ x) * 0x45d9f3b;
    x = ((x >> 16) ^ x) * 0x45d9f3b;
    x = (x >> 16) ^ x;
    return x;
}

मैजिक नंबर की गणना एक विशेष मल्टी-थ्रेडेड टेस्ट प्रोग्राम का उपयोग करके की गई थी जो कई घंटों तक चलता था, जो हिमस्खलन प्रभाव की गणना करता है (आउटपुट बिट्स की संख्या जो एकल इनपुट बिट बदल जाती है; औसतन लगभग 16 होनी चाहिए), स्वतंत्रता आउटपुट बिट परिवर्तन (आउटपुट बिट्स एक दूसरे पर निर्भर नहीं होना चाहिए), और यदि कोई इनपुट बिट बदला जाता है तो प्रत्येक आउटपुट बिट में बदलाव की संभावना। गणना किए गए मान मुरमुरैश द्वारा उपयोग किए जाने वाले 32-बिट फाइनल से बेहतर हैं , और एईएस का उपयोग करते समय लगभग (जितना अच्छा नहीं) । एक मामूली लाभ यह है कि एक ही निरंतर का उपयोग दो बार किया जाता है (यह पिछली बार जब मैंने परीक्षण किया तो यह थोड़ा तेज हो गया, यकीन नहीं होता कि यह अभी भी मामला है)।

यदि आप ( गुणक प्रतिलोम ) के 0x45d9f3bसाथ प्रतिस्थापित करते हैं, तो आप प्रक्रिया को उल्टा कर सकते हैं (हैश से इनपुट मान प्राप्त करें ):0x119de1f3

unsigned int unhash(unsigned int x) {
    x = ((x >> 16) ^ x) * 0x119de1f3;
    x = ((x >> 16) ^ x) * 0x119de1f3;
    x = (x >> 16) ^ x;
    return x;
}

64-बिट संख्या के लिए, मैं निम्नलिखित का उपयोग करने का सुझाव देता हूं, यहां तक ​​कि सोचा कि यह सबसे तेज़ नहीं हो सकता है। यह एक स्प्लिटमिक्स 64 पर आधारित है , जो ब्लॉग के लेख बेटर बिट मिक्सिंग (मिक्स 13) पर आधारित लगता है ।

uint64_t hash(uint64_t x) {
    x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
    x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
    x = x ^ (x >> 31);
    return x;
}

जावा के लिए, का उपयोग करें long, Lनिरंतर में जोड़ें , के >>साथ बदलें >>>और निकालें unsigned। इस मामले में, पलटाव अधिक जटिल है:

uint64_t unhash(uint64_t x) {
    x = (x ^ (x >> 31) ^ (x >> 62)) * UINT64_C(0x319642b2d24d8ec3);
    x = (x ^ (x >> 27) ^ (x >> 54)) * UINT64_C(0x96de1b173f119089);
    x = x ^ (x >> 30) ^ (x >> 60);
    return x;
}

अद्यतन: आप हैश फंक्शन प्रॉस्पेक्टर प्रोजेक्ट को भी देखना चाहते हैं , जहाँ अन्य (संभवतः बेहतर) स्थिरांक सूचीबद्ध हैं।


2
पहली दो पंक्तियाँ बिल्कुल एक जैसी हैं! क्या यहाँ कोई टाइपो है?
क्षितिज बैनर्जी

3
नहीं, यह टाइपो नहीं है, दूसरी पंक्ति आगे बिट्स को मिलाती है। सिर्फ एक गुणा का उपयोग करना उतना अच्छा नहीं है।
थॉमस म्यूएलर

3
मैंने जादू की संख्या को बदल दिया क्योंकि एक परीक्षण के मामले के अनुसार मैंने लिखा था मान 0x45d9f3b बेहतर भ्रम और प्रसार प्रदान करता है , विशेष रूप से कि अगर एक आउटपुट बिट बदलता है, तो एक ही संभावना के बारे में एक दूसरे के आउटपुट बिट में परिवर्तन होता है (सभी आउटपुट परिवर्तन के साथ उसी संभावना यदि कोई इनपुट बिट बदलता है)। आपने कैसे मापा 0x3335b369 आपके लिए बेहतर काम करता है? क्या आपके लिए इंट 32 बिट है?
थॉमस म्यूएलर

3
मैं 64 बिट अहस्ताक्षरित int 32 बिट अहस्ताक्षरित int के लिए एक अच्छा हैश समारोह के लिए खोज रहा हूँ। क्या उस मामले के लिए, जादू की संख्या से अधिक समान होगा? मैं 16 बिट के बजाय 32 बिट स्थानांतरित कर दिया।
एलेसैंड्रो

3
मेरा मानना ​​है कि उस मामले में एक बड़ा कारक बेहतर होगा, लेकिन आपको कुछ परीक्षण चलाने की आवश्यकता होगी। या (यह मैं क्या करता हूं) पहले उपयोग करें x = ((x >> 32) ^ x)और फिर ऊपर 32 बिट गुणा का उपयोग करें। मुझे यकीन नहीं है कि बेहतर क्या है। आप मुरमुर 3 के लिए 64-बिट फ़ाइनलीज़र
थॉमस मुलर

29

निर्भर करता है कि आपका डेटा कैसे वितरित किया जाता है। एक सरल काउंटर के लिए, सबसे सरल फ़ंक्शन

f(i) = i

अच्छा होगा (मुझे इष्टतम पर संदेह है, लेकिन मैं इसे साबित नहीं कर सकता)।


3
इसके साथ समस्या यह है कि पूर्णांकों के बड़े सेटों का होना आम है जो एक सामान्य कारक (शब्द-संरेखित मेमोरी एड्रेस आदि) द्वारा विभाज्य हैं। अब यदि आपकी हैश टेबल एक ही कारक से विभाज्य होती है, तो आप उपयोग किए गए केवल आधे (या 1/4, 1/8, आदि) बाल्टी के साथ समाप्त होते हैं।
राफेल डाउगर्ड 16

8
@ रफाल: यही कारण है कि प्रतिक्रिया "एक साधारण काउंटर के लिए" और "इस बात पर निर्भर करती है कि आपका डेटा कैसे वितरित किया जाता है"
erikkallen

5
यह वास्तव में java.lang.Integer grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdhp//
Juande Carrion

5
@JuandeCarrion यह भ्रामक है क्योंकि इसका उपयोग किया जाने वाला हैश नहीं है। दो तालिका आकार की शक्ति का उपयोग करने के लिए आगे बढ़ने के बाद, जावा हर हैश से वापस आ गया .hashCode(), यहां देखें ।
एसेलीजा

8
पहचान का कार्य अपने वितरण गुणों (या इसके अभाव) के कारण कई व्यावहारिक अनुप्रयोगों में हैश के रूप में काफी बेकार है, जब तक कि निश्चित रूप से, स्थानीयता एक वांछित विशेषता है
awdz9nld

12

तेज और अच्छे हैश कार्यों को कम गुणों के साथ तेजी से क्रमपरिवर्तन से बनाया जा सकता है, जैसे

  • असमान पूर्णांक के साथ गुणा
  • बाइनरी घुमाव
  • xorshift

बेहतर गुणों के साथ एक हैशिंग फ़ंक्शन का उत्पादन करने के लिए, जैसे यादृच्छिक संख्या पीढ़ी के लिए पीसीजी के साथ प्रदर्शन किया गया ।

यह वास्तव में रेसिपी rrxmrxmsx_0 है और बड़बड़ाहट हैश जानबूझकर या अनजाने में उपयोग कर रहे हैं।

मैं व्यक्तिगत रूप से मिला

uint64_t xorshift(const uint64_t& n,int i){
  return n^(n>>i);
}
uint64_t hash(const uint64_t& n){
  uint64_t p = 0x5555555555555555ull; // pattern of alternating 0 and 1
  uint64_t c = 17316035218449499591ull;// random uneven integer constant; 
  return c*xorshift(p*xorshift(n,32),32);
}

काफी अच्छा है।

एक अच्छा हैश फ़ंक्शन होना चाहिए

  1. यदि संभव हो तो ढीली जानकारी के लिए विशेषण न हो, और कम से कम टकराव हो
  2. कैस्केड जितना संभव हो उतना और समान रूप से, अर्थात प्रत्येक इनपुट बिट को प्रत्येक आउटपुट बिट को प्रायिकता 0.5 के साथ फ्लिप करना चाहिए।

आइए सबसे पहले आइडेंटिटी फंक्शन देखें। यह 1. संतुष्ट करता है, लेकिन 2. नहीं:

पहचान समारोह

इनपुट बिट n 100% (लाल) के सहसंबंध के साथ आउटपुट बिट n निर्धारित करता है और कोई अन्य नहीं, इसलिए वे नीले हैं, जो संपूर्ण लाल रेखा को पार करते हैं।

एक xorshift (n, 32) ज्यादा बेहतर नहीं है, एक और आधी लाइन की पैदावार। अभी भी संतोषजनक 1., क्योंकि यह एक दूसरे अनुप्रयोग के साथ उलटा है।

xorshift

एक अहस्ताक्षरित पूर्णांक के साथ एक गुणन बहुत बेहतर होता है, अधिक मजबूती से कैस्केडिंग करता है और 0.5 की संभावना के साथ अधिक आउटपुट बिट्स को फ्लिप करता है, जो कि आप चाहते हैं, हरे रंग में। यह 1 को संतुष्ट करता है। प्रत्येक असमान पूर्णांक के लिए एक गुणात्मक व्युत्क्रम होता है।

नुथ

दो को मिलाने से निम्न आउटपुट प्राप्त होता है, फिर भी संतोषजनक 1। दो विशेषण कार्यों की संरचना के कारण एक और विशेषण फलित होता है।

नुथ • xorshift

गुणन और xorshift का एक दूसरा आवेदन निम्नलिखित प्राप्त करेगा:

प्रस्तावित हैश

या आप गलास जैसे गुणेश क्षेत्र गुणकों का उपयोग कर सकते हैं , वे आधुनिक सीपीयू पर यथोचित रूप से तेज़ हो गए हैं और एक चरण में बेहतर गुण हैं।

   uint64_t const inline gfmul(const uint64_t& i,const uint64_t& j){           
     __m128i I{};I[0]^=i;                                                          
     __m128i J{};J[0]^=j;                                                          
     __m128i M{};M[0]^=0xb000000000000000ull;                                      
     __m128i X = _mm_clmulepi64_si128(I,J,0);                                      
     __m128i A = _mm_clmulepi64_si128(X,M,0);                                      
     __m128i B = _mm_clmulepi64_si128(A,M,0);                                      
     return A[0]^A[1]^B[1]^X[0]^X[1];                                              
   }

gfmul: कोड छद्म कोड प्रतीत होता है, क्योंकि afaik से आप __m128i के साथ कोष्ठक का उपयोग नहीं कर सकते हैं। फिर भी बहुत दिलचस्प है। पहली पंक्ति यह कहती दिखाई देती है कि "एक इकाईीकृत __m128i (I) लें और इसे (पैरामीटर) के साथ xor करें। क्या मुझे इसे 0 के साथ प्रारंभ के रूप में पढ़ना चाहिए? और मैं पर एक नहीं (आपरेशन) कारगर साबित होंगे?
जनवरी

@ जो मैं चाहता हूं वह करना है __m128i I = i; //set the lower 64 bits, लेकिन मैं ऐसा नहीं कर सकता, इसलिए मैं उपयोग कर रहा हूं ^=0^1 = 1इसलिए कोई अवांछित नहीं है। साथ initialisation के बारे में {}कभी नहीं शिकायत की मेरी संकलक, यह सबसे अच्छा समाधान नहीं हो सकता है, लेकिन क्या मैं उस के साथ चाहते हैं 0 को इसके बारे में सब आरंभ है तो मैं क्या कर सकते हैं ^=या |=। मुझे लगता है कि मैं इस ब्लॉगपोस्ट पर उस कोड को आधारित करता हूं, जो उलटा भी देता है, बहुत उपयोगी है: डी
वोल्फगैंग ब्रिम्म

6

यह पृष्ठ कुछ सरल हैश फ़ंक्शन को सूचीबद्ध करता है जो सामान्य रूप से शालीनता से करते हैं, लेकिन किसी भी सरल हैश में पैथोलॉजिकल मामले हैं जहां यह अच्छी तरह से काम नहीं करता है।



3

अनंत काल में कुछ हैश एल्गोरिदम पर एक अच्छा अवलोकन है । मैं बॉब जेनकींस की एक-पर-एक-समय हैश की सिफारिश करूंगा, जो जल्दी से हिमस्खलन तक पहुंच जाता है और इसलिए इसे कुशल हैशट टेबल लुकअप के लिए इस्तेमाल किया जा सकता है।


4
यह एक अच्छा लेख है, लेकिन यह हैशिंग स्ट्रिंग कुंजियों पर केंद्रित है, पूर्णांक नहीं।
एड्रियन मौट

बस स्पष्ट होने के लिए, हालांकि लेख के तरीके पूर्णांक के लिए काम करेंगे (या अनुकूलित किया जा सकता है), मुझे लगता है कि पूर्णांक के लिए अधिक कुशल एल्गोरिदम हैं।
एड्रियन मौट

2

जवाब बहुत सी बातों पर निर्भर करता है जैसे:

  • आप इसे कहाँ नियोजित करना चाहते हैं?
  • आप हैश के साथ क्या करने की कोशिश कर रहे हैं?
  • क्या आपको क्रोधित रूप से सुरक्षित हैश फ़ंक्शन की आवश्यकता है?

मेरा सुझाव है कि आप SHA-1 इत्यादि जैसे हैश कार्यों के मर्कले-डमगार्ड परिवार पर एक नज़र डालें


1

मुझे नहीं लगता कि हम कह सकते हैं कि एक हैश फ़ंक्शन आपके डेटा को अग्रिम में जानने के बिना "अच्छा" है! और यह जाने बिना कि आप इसके साथ क्या करने जा रहे हैं।

अज्ञात डेटा आकारों के लिए हैश टेबल की तुलना में बेहतर डेटा संरचनाएं हैं (मैं मान रहा हूं कि आप यहां हैश टेबल के लिए हैशिंग कर रहे हैं)। मैं व्यक्तिगत रूप से एक हैश टेबल का उपयोग करूंगा जब मुझे पता चलेगा कि मेरे पास "परिमित" तत्वों की संख्या है जिन्हें स्मृति की सीमित मात्रा में संग्रहित करने की आवश्यकता है। मैं अपने डेटा पर त्वरित सांख्यिकीय विश्लेषण करने की कोशिश करूँगा, यह देखूंगा कि मेरे हैश फ़ंक्शन के बारे में सोचने से पहले इसे कैसे वितरित किया जाता है आदि।


1

यादृच्छिक हैश मूल्यों के लिए, कुछ इंजीनियरों ने कहा कि स्वर्ण अनुपात अभाज्य संख्या (2654435761) एक बुरा विकल्प है, मेरे परीक्षण परिणामों के साथ, मैंने पाया कि यह सच नहीं है; इसके बजाय, 2654435761 हैश मूल्यों को बहुत अच्छा वितरित करता है।

#define MCR_HashTableSize 2^10

unsigned int
Hash_UInt_GRPrimeNumber(unsigned int key)
{
  key = key*2654435761 & (MCR_HashTableSize - 1)
  return key;
}

हैश टेबल का आकार दो की शक्ति होना चाहिए।

मैंने पूर्णांक के लिए कई हैश फ़ंक्शन का मूल्यांकन करने के लिए एक परीक्षण कार्यक्रम लिखा है, परिणाम बताते हैं कि GRPrimeNumber एक बहुत अच्छा विकल्प है।

मैंने कोशिश की है:

  1. Total_data_entry_number / total_bucket_number = 2, 3, 4; जहाँ Total_bucket_number = हैश तालिका आकार;
  2. बकेट इंडेक्स डोमेन में मैप हैश मान डोमेन; वह है, तार्किक और ऑपरेशन के द्वारा हैश मान को बकेट इंडेक्स में बदलें (हैश_टेबल_साइज़ - 1), जैसा कि Hash_UInt_GRPrimeNumber () में दिखाया गया है;
  3. प्रत्येक बाल्टी की टक्कर संख्या की गणना करें;
  4. उस बाल्टी को रिकॉर्ड करें जिसे मैप नहीं किया गया है, यानी एक खाली बाल्टी;
  5. सभी बाल्टियों की अधिकतम टक्कर संख्या ज्ञात करें; वह है, सबसे लंबी श्रृंखला लंबाई;

अपने परीक्षण परिणामों के साथ, मैंने पाया कि गोल्डन रेशियो प्राइम नंबर में हमेशा कम खाली बाल्टी या शून्य खाली बाल्टी और सबसे छोटी कोलरी श्रृंखला की लंबाई होती है।

पूर्णांकों के लिए कुछ हैश फ़ंक्शन अच्छे होने का दावा किया जाता है, लेकिन परीक्षण के परिणाम बताते हैं कि जब कुल_data_entry / total_bucket_number = 3, सबसे लंबी श्रृंखला की लंबाई 10 (अधिकतम टकराव की संख्या> 10) से अधिक होती है, और कई बाल्टी मैप नहीं की जाती हैं (खाली बाल्टी ), जो कि गोल्डन रैट प्राइम नंबर हाशिंग द्वारा शून्य खाली बाल्टी और सबसे लंबी श्रृंखला लंबाई 3 के परिणाम की तुलना में बहुत खराब है।

BTW, मेरे परीक्षण परिणामों के साथ, मैंने पाया कि शिफ्टिंग-एक्सआर हैश फ़ंक्शन का एक संस्करण बहुत अच्छा है (यह मीकेरा द्वारा साझा किया गया है)।

unsigned int Hash_UInt_M3(unsigned int key)
{
  key ^= (key << 13);
  key ^= (key >> 17);    
  key ^= (key << 5); 
  return key;
}

2
लेकिन फिर उत्पाद को सही क्यों नहीं स्थानांतरित किया जाता है, इसलिए आप सबसे मिश्रित बिट्स रखते हैं? जिस तरह से यह काम करना चाहिए था
हेरोल्ड

1
@ हेरोल्ड, गोल्डन अनुपात प्राइम नंबर को ध्यान से चुना जाता है, हालांकि मुझे लगता है कि इससे कोई फर्क नहीं पड़ेगा, लेकिन मैं यह देखने के लिए परीक्षण करूंगा कि क्या यह "सबसे मिश्रित बिट्स" के साथ बेहतर है। जबकि मेरी बात यह है कि "यह एक अच्छा विकल्प नहीं है।" यह सच नहीं है, जैसा कि परीक्षण के परिणाम दिखाते हैं, बस बिट्स के निचले हिस्से को पकड़ना काफी अच्छा है, और कई हैश कार्यों से भी बेहतर है।
चेन-चुंगछिया

(२६५४४३५ 26६१, ४२ ९ ५२०३४ is ९) अपराधों का स्वर्णिम अनुपात है।
चेन-चुंगछिया

(1640565991, 2654435761) भी अपराधों का सुनहरा अनुपात है।
चेन-चुंगचिया

@ हेरोल्ड, उत्पाद अधिकार को शिफ्ट करना और भी बदतर हो जाता है, भले ही सिर्फ 1 स्थिति (2 से विभाजित) द्वारा सही स्थानांतरण हो, यह अभी भी बदतर हो जाता है (हालांकि अभी भी शून्य खाली बाल्टी है, लेकिन सबसे लंबी श्रृंखला की लंबाई बड़ी है); अधिक पदों द्वारा सही स्थानांतरण, परिणाम और भी बदतर हो जाता है। क्यों? मुझे लगता है कि इसका कारण है: उत्पाद को सही तरीके से स्थानांतरित करना अधिक हैश मूल्यों को कॉपीराइम नहीं होने देता है, बस मेरा अनुमान है, वास्तविक कारण में सिद्धांत शामिल है।
चेन-चुंगचिया

1

मैं splitmix64(थॉमस मुलेर के उत्तर में इंगित ) का उपयोग कर रहा हूं जब से मैंने यह धागा पाया है। हालाँकि, मैंने हाल ही में पेले इवेंसन के rxxrrxmsx_0 पर ठोकर खाई , जो मूल मुरमुरश 3 फाइनल और उसके उत्तराधिकारियों ( splitmix64और अन्य मिक्स) की तुलना में काफी बेहतर सांख्यिकीय वितरण प्राप्त हुआ । यहाँ C में कोड स्निपेट है:

#include <stdint.h>

static inline uint64_t ror64(uint64_t v, int r) {
    return (v >> r) | (v << (64 - r));
}

uint64_t rrxmrrxmsx_0(uint64_t v) {
    v ^= ror64(v, 25) ^ ror64(v, 50);
    v *= 0xA24BAED4963EE407UL;
    v ^= ror64(v, 24) ^ ror64(v, 49);
    v *= 0x9FB21C651E98DF25UL;
    return v ^ v >> 28;
}

पेले 64-बिट मिक्सर के अंतिम चरण में और अधिक हाल के वेरिएंट में उपयोग किए गए गहन विश्लेषण भी प्रदान करता है MurmurHash3


2
यह फ़ंक्शन बायजेक्टिव नहीं है। सभी v के लिए जहां v = ror (v, 25), अर्थात् सभी 0 और सभी 1 यह दो स्थानों पर एक ही आउटपुट का उत्पादन करेगा। सभी मानों के लिए v = ror64 (v, 24) ^ ror64 (v, 49), जो कि कम से कम दो अधिक हैं और v = ror (v, 28) के साथ समान हैं, अन्य 2 ^ 4 की पैदावार, कुल मिलाकर लगभग 22 बराबर टक्कर । स्प्लिटमिक्स के दो अनुप्रयोग शायद उतने ही अच्छे और उतने ही तेज़ हैं, लेकिन फिर भी उलटे और टकराव से मुक्त हैं।
वोल्फगैंग ब्रेहम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.