मनमाना यादृच्छिकता (गति संस्करण)


10

पूर्णांक को देखते हुए n, nश्रेणी 1..n^2(समावेशी) में यादृच्छिक अद्वितीय पूर्णांक के एक सेट की गणना करें, जैसे कि सेट का योग बराबर हैn^2

यादृच्छिक, इस मामले में, वैध आउटपुट के बीच समान रूप से यादृच्छिक का मतलब है । किसी दिए गए प्रत्येक वैध आउटपुट के लिए nएक समान अवसर होना चाहिए।

उदाहरण के लिए, n=3एक 1/3 मौका outputting से प्रत्येक होना चाहिए 6, 1, 2, 3, 5, 1या 4, 3, 2। जैसा कि यह एक सेट है, आदेश अप्रासंगिक है, 4, 3, 2के समान है3, 2, 4

स्कोरिंग

विजेता वह कार्यक्रम है जो n60 सेकंड से कम समय में उच्चतम गणना कर सकता है ।
नोट: संभव आंशिक हार्डकोडिंग को रोकने के लिए, सभी प्रविष्टियों को 4000 बाइट्स के नीचे होना चाहिए

परिक्षण

सभी कोड मेरे स्थानीय विंडोज 10 मशीन (रेजर ब्लेड 15, 16 जीबी रैम, इंटेल i7-8750H 6 कोर, 4.1GHz, GTX 1060 के मामले में आप GPU का दुरुपयोग करना चाहते हैं) पर चलाया जाएगा ताकि कृपया इस कोड को चलाने के लिए विस्तृत निर्देश प्रदान करें मेरी मशीन।
अनुरोध पर, प्रविष्टियाँ WSL पर डेबियन के माध्यम से या Xubuntu वर्चुअल मशीन (दोनों मशीन पर समान रूप से) पर चलाई जा सकती हैं

उत्तराधिकार में 50 बार सबमिशन चलाया जाएगा, अंतिम स्कोर सभी 50 परिणामों का औसत होगा।



क्या हार्डकोडिंग की अनुमति थोड़ी है, अगर यह 4000 बाइट्स के अंतर्गत है?
क्विंटेक

@ क्विंटेक नहीं, हार्डकोडिंग एक मानक खामी है, इसलिए डिफ़ॉल्ट रूप से निषिद्ध है। मुश्किल बात यह है कि हार्डकोडिंग को भी एक अप्राप्य मानदंड माना जाता है, इसलिए मैं आधिकारिक तौर पर यह नहीं कह सकता कि लोफोल को नापसंद करने से परे "कोई हार्डकॉडिंग" नहीं है। इसलिए बाइट की सीमा। दूसरे शब्दों में: कृपया हार्डकोड
स्काईड्सविंड

1
अधिकांश प्रस्तुतियाँ एक अस्वीकृति विधि का उपयोग करें और इस प्रकार चल रहा समय यादृच्छिक होगा और बड़ी परिवर्तनशीलता होगी। यह मुश्किल समय बनाता है
लुइस मेंडो

2
ओह, मैं भूल गया - क्योंकि कुछ समाधान कम गुणवत्ता वाले आरएनजी का उपयोग करने का निर्णय ले सकते हैं ताकि यह तेज़ हो, एक ब्लैक बॉक्स दिनचर्या प्रदान करना आवश्यक हो सकता है जो n लेता है और (1..n) में एक यादृच्छिक संख्या उत्पन्न करता है, और सभी को बल देता है इसका उपयोग करने के लिए उपाय।
user202729

जवाबों:


6

जंग , एन ≈ 1400

कैसे चलाना है?

साथ बनाएँ cargo build --release, और साथ चलाएँ target/release/arbitrary-randomness n

यह कार्यक्रम बहुत सारी मेमोरी के साथ सबसे तेज चलता है (जब तक कि यह स्वैप नहीं हो रहा है, निश्चित रूप से)। आप MAX_BYTESवर्तमान में 8 GiB पर सेट निरंतर को संपादित करके इसके मेमोरी उपयोग को समायोजित कर सकते हैं ।

यह काम किस प्रकार करता है

सेट का निर्माण द्विआधारी निर्णयों के अनुक्रम द्वारा किया गया है (प्रत्येक संख्या या तो सेट के अंदर या बाहर है), जिनकी प्रत्येक संभाव्यता को गतिशील प्रोग्रामिंग का उपयोग करके प्रत्येक पसंद के बाद संभव सेट की संख्या की गणना करके संयुक्त रूप से गणना की जाती है।

इस बाइनोमियल विभाजन रणनीति के एक संस्करण का उपयोग करके बड़े एन के लिए मेमोरी का उपयोग कम किया जाता है ।

Cargo.toml

[package]
name = "arbitrary-randomness"
version = "0.1.0"
authors = ["Anders Kaseorg <andersk@mit.edu>"]

[dependencies]
rand = "0.6"

src/main.rs

extern crate rand;

use rand::prelude::*;
use std::env;
use std::f64;
use std::mem;

const MAX_BYTES: usize = 8 << 30; // 8 gibibytes

fn ln_add_exp(a: f64, b: f64) -> f64 {
    if a > b {
        (b - a).exp().ln_1p() + a
    } else {
        (a - b).exp().ln_1p() + b
    }
}

fn split(steps: usize, memory: usize) -> usize {
    if steps == 1 {
        return 0;
    }
    let mut u0 = 0;
    let mut n0 = f64::INFINITY;
    let mut u1 = steps;
    let mut n1 = -f64::INFINITY;
    while u1 - u0 > 1 {
        let u = (u0 + u1) / 2;
        let k = (memory * steps) as f64 / u as f64;
        let n = (0..memory)
            .map(|i| (k - i as f64) / (i as f64 + 1.))
            .product();
        if n > steps as f64 {
            u0 = u;
            n0 = n;
        } else {
            u1 = u;
            n1 = n;
        }
    }
    if n0 - (steps as f64) <= steps as f64 - n1 {
        u0
    } else {
        u1
    }
}

fn gen(n: usize, rng: &mut impl Rng) -> Vec<usize> {
    let s = n * n.wrapping_sub(1) / 2;
    let width = n.min(MAX_BYTES / ((s + 1) * mem::size_of::<f64>()));
    let ix = |m: usize, k: usize| m + k * (s + 1);
    let mut ln_count = vec![-f64::INFINITY; ix(0, width)];
    let mut checkpoints = Vec::with_capacity(width);
    let mut a = Vec::with_capacity(n);
    let mut m = s;
    let mut x = 1;

    for k in (1..=n).rev() {
        let i = loop {
            let i = checkpoints.len();
            let k0 = *checkpoints.last().unwrap_or(&0);
            if k0 == k {
                checkpoints.pop();
                break i - 1;
            }
            if i == 0 {
                ln_count[ix(0, i)] = 0.;
                for m in 1..=s {
                    ln_count[ix(m, i)] = -f64::INFINITY;
                }
            } else {
                for m in 0..=s {
                    ln_count[ix(m, i)] = ln_count[ix(m, i - 1)];
                }
            }
            let k1 = k - split(k - k0, width - 1 - i);
            for step in k0 + 1..=k1 {
                for m in step..=s {
                    ln_count[ix(m, i)] = ln_add_exp(ln_count[ix(m - step, i)], ln_count[ix(m, i)]);
                }
            }
            if k1 == k {
                break i;
            }
            checkpoints.push(k1);
        };

        while m >= k && rng.gen_bool((ln_count[ix(m - k, i)] - ln_count[ix(m, i)]).exp()) {
            m -= k;
            x += 1;
        }
        a.push(x);
        x += 1;
    }
    a
}

fn main() {
    if let [_, n] = &env::args().collect::<Vec<_>>()[..] {
        let n = n.parse().unwrap();
        let mut rng = StdRng::from_entropy();
        println!("{:?}", gen(n, &mut rng));
    } else {
        panic!("expected one argument");
    }
}

इसे ऑनलाइन आज़माएं!

(ध्यान दें: TIO संस्करण में कुछ संशोधन हैं। सबसे पहले, मेमोरी की सीमा 1 GiB तक कम कर दी जाती है। दूसरा, चूंकि TIO आपको लिखने नहीं देता है Cargo.tomlऔर बाहरी क्रेटों पर निर्भर करता है rand, जैसे कि मैंने इसके बजाय drand48C लाइब्रेरी का उपयोग करके C लाइब्रेरी से खींच लिया है । एफएफआई। मैंने इसे बोने की जहमत नहीं उठाई, इसलिए टीआईओ संस्करण हर रन पर एक ही परिणाम देगा। आधिकारिक बेंचमार्किंग के लिए टीआईओ संस्करण का उपयोग न करें।)


चूँकि फ़्लोटिंग पॉइंट प्रारूप परिमित है, इसलिए यह ln_add_expजाँच कर अनुकूलित करना संभव है कि क्या पूर्ण अंतर ~ 15 या इससे अधिक है, जो कि इस तरह के बहुत से अतिरिक्त होने पर तेज़ हो सकता है।
user202729

@ user202729 नहीं, लगभग सभी ln_add_expकॉल्स में तुलनीय इनपुट शामिल हैं।
एंडर्स कासोर्ग

3

TIO पर जावा 7+, n = 50 ~ 30 सेकंड में

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Random;
class Main{
  public static void main(String[] a){

    int n=50;

    Random randomGenerator = new Random();
    int i = n+1;
    int squaredN = n*n;
    int[]randomIntegers = new int[i];
    randomIntegers[n] = squaredN;
    while(true){
      for(i=n; i-->1; ){
        randomIntegers[i] = randomGenerator.nextInt(squaredN);
      }
      Set<Integer> result = new HashSet<>();
      Arrays.sort(randomIntegers);
      for(i=n; i-->0; ){
        result.add(randomIntegers[i+1] - randomIntegers[i]);
      }
      if(!result.contains(0) && result.size()==n){
        System.out.println(result);
        return;
      }
    }
  }
}

इस चुनौती के कोड-गोल्फ संस्करण के लिए मेरे जवाब का अब तक का केवल अनौपचारिक संस्करण , केवल एक मामूली बदलाव के साथ: श्रेणी में पूर्णांक java.util.Random#nextInt(limit)के (int)(Math.random()*limit)लिए उपयोग किया जाता है [0, n), क्योंकि यह लगभग दोगुना है

इसे ऑनलाइन आज़माएं।

स्पष्टीकरण:

दृष्टिकोण का इस्तेमाल किया:

कोड दो भागों में विभाजित है:

  1. nरैंडम पूर्णांकों की राशि की एक सूची बनाएं जो कि योग है n squared
  2. तब यह जांचता है कि क्या सभी मूल्य अद्वितीय हैं और कोई भी शून्य नहीं है, और यदि या तो गलत है, तो यह फिर से चरण 1 की कोशिश करेगा, जब तक हमारे पास परिणाम नहीं होगा तब तक दोहराते और दोहराते रहें।

चरण 1 निम्नलिखित उप-चरणों के साथ किया जाता है:

1) n-1रेंज में यादृच्छिक पूर्णांकों की मात्रा का एक सरणी उत्पन्न करते हैं [0, n squared)। और इस सूची में जोड़ें 0और n squared। यह O(n+1)प्रदर्शन में किया जाता है ।
2) फिर यह बिलियन के साथ सरणी को सॉर्ट करेगा java.util.Arrays.sort(int[]), यह O(n*log(n))प्रदर्शन में किया जाता है , जैसा कि डॉक्स में बताया गया है:

संख्यात्मक क्रम में आरोही की निर्दिष्ट सरणी को सॉर्ट करता है। सॉर्टिंग एल्गोरिथ्म एक ट्यून किया गया क्विकॉर्ट है, जो जॉन एल। बेंटले और एम। डगलस मैकलियर के "इंजीनियरिंग ए सॉर्ट फंक्शन", सॉफ्टवेयर-प्रैक्टिस और एक्सपीरियंस, वॉल्यूम से लिया गया है। 23 (11) पी। 1249-1265 (नवंबर 1993)। यह एल्गोरिथ्म कई डेटा सेट पर n * लॉग (n) प्रदर्शन प्रदान करता है जो अन्य क्विकॉर्ट्स को द्विघात प्रदर्शन को नीचा दिखाने का कारण बनता है।

3) प्रत्येक जोड़ी के बीच अंतर की गणना करें। अंतर की इस परिणामी सूची में nपूर्णांकों के योग होंगे जो योग करते हैं n squared। यह O(n)प्रदर्शन में किया जाता है ।

यहाँ एक उदाहरण है:

// n = 4, nSquared = 16

// n-1 amount of random integers in the range [0, nSquared):
[11, 2, 5]

// Add 0 and nSquared to it, and sort:
[0, 2, 5, 11, 16]

// Calculate differences:
[2, 3, 6, 5]

// The sum of these differences will always be equal to nSquared
sum([2, 3, 6, 5]) = 16

तो ऊपर दिए गए ये तीन चरण प्रदर्शन के लिए बहुत अच्छे हैं, चरण 2 के विपरीत और पूरी चीज़ के चारों ओर लूप, जो एक बुनियादी जानवर-बल है। चरण 2 इन उप-चरणों में विभाजित है:

1) अंतर सूची पहले से ही में सहेजी गई है java.util.Set। यह जांच करेगा कि क्या इस सेट का आकार बराबर है n। यदि ऐसा है, तो इसका मतलब है कि हमारे द्वारा उत्पन्न सभी यादृच्छिक मूल्य अद्वितीय हैं।
2) और यह भी जाँच करेगा कि यह कोई शामिल है 0के बाद से चुनौती रेंज में यादृच्छिक मान के लिए पूछता है, सेट में [1, X], जहां Xहै n squaredशून्य से की राशि [1, ..., n-1]के रूप में द्वारा कहा गया, @Skidsdev नीचे टिप्पणी में।

यदि उपरोक्त दोनों विकल्पों में से कोई भी (सभी मान अद्वितीय नहीं हैं, या एक शून्य मौजूद है), तो यह चरण 1 पर रीसेट करके एक नया सरणी उत्पन्न करेगा और फिर सेट करेगा। यह तब तक जारी रहता है जब तक कि हमारे पास कोई परिणाम न हो। इस वजह से, समय काफी भिन्न हो सकता है। मैंने इसे TIO पर एक बार 3 सेकंड में पूरा करने के लिए देखा है n=50, लेकिन एक बार के लिए 55 सेकंड में भी n=50

एकरूपता साबित करें:

मुझे पूरी तरह से यकीन नहीं है कि इसे पूरी तरह से ईमानदार कैसे साबित किया जाए। java.util.Random#nextIntके रूप में डॉक्स में वर्णन किया गया है, यकीन है कि के लिए एक समान है:

intइस यादृच्छिक संख्या जनरेटर के अनुक्रम से समान रूप से वितरित अगले छद्म आयामी, लौटाता है । इसका सामान्य अनुबंध nextIntयह है कि एक intमान छद्म रूप से उत्पन्न होता है और वापस लौटता है। सभी 2 32 संभावित intमानों (लगभग) समान संभावना के साथ उत्पादन किया जाता है।

इन (छांटे गए) यादृच्छिक मूल्यों के बीच अंतर निश्चित रूप से एक समान नहीं है, लेकिन एक पूरे के रूप में सेट समान हैं। फिर, मुझे यकीन नहीं है कि इसे गणितीय रूप से कैसे साबित किया जाए, लेकिन यहां एक स्क्रिप्ट है जो काउंटर के साथ मैप में 10,000उत्पन्न सेट (के लिए n=10) डाल देगा , जहां अधिकांश सेट अद्वितीय हैं; कुछ दो बार दोहराया; और अधिकतम बार-बार होने वाली घटना सीमा में है [4,8]

स्थापाना निर्देश:

चूँकि जावा कोड को बनाने और चलाने के बारे में बहुत सारी जानकारी उपलब्ध है, इसलिए जावा एक बहुत ही जानी-मानी भाषा है, इसलिए मैं इसे छोटा रखूँगा।
मेरे कोड में उपयोग किए जाने वाले सभी उपकरण जावा 7 (शायद पहले से ही जावा 5 या 6 में उपलब्ध हैं, लेकिन चलो केवल 7 का उपयोग करें)। मुझे पूरा यकीन है कि जावा 7 पहले से ही संग्रहीत है, इसलिए मैं अपना कोड चलाने के लिए जावा 8 डाउनलोड करने का सुझाव दूंगा।

सुधार के बारे में विचार:

मैं शून्य के लिए चेक के लिए एक सुधार खोजना चाहता हूं और सभी मान अद्वितीय हैं। मैं 0पहले से जांच कर सकता था , यह सुनिश्चित करने के लिए कि हम जिस यादृच्छिक मान को जोड़ते हैं, वह सरणी में पहले से ही नहीं है, लेकिन इसका मतलब होगा कि कुछ चीजें: सरणी एक होनी चाहिए ArrayListताकि हम बिलिन पद्धति का उपयोग कर सकें .contains; थोड़ी देर के लूप को तब तक जोड़ा जाना चाहिए जब तक हमें एक यादृच्छिक मान नहीं मिल जाता है जो अभी तक सूची में नहीं है। चूंकि शून्य के लिए जाँच अब .contains(0)सेट पर की जाती है (जो केवल एक बार जाँच की जाती है), यह उस बिंदु पर जाँच करने के लिए प्रदर्शन के लिए सबसे बेहतर है .contains, सूची में लूप को जोड़ने की तुलना में , जिसे कम से कम nबार चेक किया जाएगा , लेकिन सबसे अधिक संभावना है।

विशिष्टता की जांच के लिए, हमारे पास केवल nयादृच्छिक पूर्णांकों की राशि है n squaredजो कार्यक्रम के चरण 1 के बाद योग करते हैं , इसलिए केवल तभी हम जांच सकते हैं कि सभी अद्वितीय हैं या नहीं। सरणी के बजाय एक छांटने योग्य सूची रखना और बीच में अंतर की जांच करना संभव हो सकता है, लेकिन मुझे गंभीरता से संदेह है कि यह प्रदर्शन को सुधारने से बेहतर होगा कि उन्हें एक में डाल दिया जाए Setऔर जांचें कि क्या उस सेट का आकार nएक बार है।


1
अगर यह गति में मदद करता है, तो सेट में कोई भी संख्या n^2 - sum(1..n-1)उदाहरण के लिए n=5सबसे बड़ी वैध संख्या से अधिक नहीं हो सकती है5^2 - sum(1, 2, 3, 4) == 25 - 10 == 15
स्काईड्सविंड

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

1
परिणामी सेट का आकार इससे अधिक कभी नहीं हो nसकता है? उस स्थिति में, आप 0सेट में जोड़ सकते हैं , और फिर जांच सकते हैं कि आकार (अब) से अधिक है n। यह केवल तभी हो सकता है जब अंतर सभी गैर-शून्य और अलग हों।
नील

@ नील ओह, यह बहुत स्मार्ट है, और मैं निश्चित रूप से कुछ बाइट्स से गोल्फ को अपने कोड-गोल्फ उत्तर में उपयोग करूंगा। मुझे यकीन नहीं है कि यह यहाँ प्रदर्शन में सुधार करेगा, हालांकि। HashSet.containsअधिकांश मामलों के करीब है O(1), और सबसे खराब स्थिति में यह O(n)जावा 7 में है, और O(log n)जावा 8+ में (इसे टक्कर का पता लगाने के साथ बदलने के बाद सुधार किया गया है)। यदि मुझे 0चेक के लिए जोड़े के साथ सेट वापस करने की अनुमति है , तो यह वास्तव में प्रदर्शन के लिए थोड़ा बेहतर है, लेकिन अगर मुझे अगर set.remove(0);अंदर कॉल करना है, तो मुझे यकीन है कि प्रदर्शन कुछ हद तक समान है।
केविन क्रूज़सेन

ओह, मैं भूल गया कि आपको सेट को वापस करने की आवश्यकता है ... कोई बात नहीं।
नील

1

गणितज्ञ n = 11

(While[Tr@(a=RandomSample[Range[#^2-#(#-1)/2],#])!=#^2];a)&     
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.