Keyfi Rastgele (Hız baskısı)


10

Tamsayı verildiğinde n, kümenin toplamının eşit olacağı şekilde bir ndizi 1..n^2( benzersiz) rastgele benzersiz tamsayılar hesaplayınn^2

Rastgele, bu durumda, geçerli çıktılar arasında eşit olarak rastgele anlamına gelir . Belirli bir verim için her geçerli çıktının neşit bir şekilde oluşturulma şansı olmalıdır.

Örneğin, n=31/3 bir şansı çıkışı her olmalı 6, 1, 2, 3, 5, 1ya da 4, 3, 2. Bu bir dizi olduğu için, sırası önemli değildir 4, 3, 2ile aynıdır3, 2, 4

puanlama

Kazanan, n60 saniyenin altında en yüksek değeri hesaplayabilen programdır .
Not: Olası kısmi kodlamayı önlemek için tüm girişler 4000 baytın altında olmalıdır

Test yapmak

Tüm kod yerel Windows 10 makinemde çalıştırılacaktır (Razer Blade 15, 16GB RAM, Intel i7-8750H 6 çekirdek, 4.1GHz, GPU'yu kötüye kullanmak istiyorsanız GTX 1060), bu nedenle lütfen kodunuzu çalıştırmak için ayrıntılı talimatlar sağlayın benim makinem.
İstekler üzerine girdiler WSL'deki Debian veya Xubuntu Sanal Makinesi üzerinde (her ikisi de yukarıdakiyle aynı makinede) çalıştırılabilir.

Başvurular art arda 50 kez yürütülecek, final puanı 50 sonucun ortalaması olacaktır.



4000 baytın altındaysa kodlama biraz izin verilir mi?
Quintec

@Quintec Hayır, sabit kodlama standart bir boşluktur, bu nedenle varsayılan olarak yasaktır. Zor bir şey hardcoding de gözlemlenemez bir kriter olarak kabul edilir, bu yüzden resmen boşluk deliği izin vermez ötesinde "Hiçbir hardcoding" diyemeyiz. Bu nedenle bayt limiti. Başka bir deyişle: Lütfen değil kod gömmek do
Skidsdev

1
Çoğu başvuru bir ret yöntemi kullanacak ve bu nedenle çalışma süresi rastgele olacak ve büyük bir değişkenliğe sahip olacaktır. Bu zamanlamayı zorlaştırır
Luis Mendo

2
Oh, unuttum - bazı çözümler hızlı olmak için düşük kaliteli RNG kullanmaya karar verebilir, n'yi alan ve (1..n) 'de rastgele bir sayı üreten ve hepsini zorlayan bir kara kutu rutini sağlamak gerekebilir. kullanmak için çözümler.
user202729

Yanıtlar:


6

Pas , n 00 1400

Nasıl çalıştırılır

İle oluşturun cargo build --releaseve ile çalıştırın target/release/arbitrary-randomness n.

Bu program çok fazla bellekle en hızlı şekilde çalışır (tabii ki değiştirmediği sürece). Bellek kullanımını MAX_BYTES, şu anda 8 GiB olarak ayarlanmış olan sabiti düzenleyerek ayarlayabilirsiniz.

Nasıl çalışır

Kümesi, her biri olasılıkları dinamik programlama kullanılarak yapılandırılabilir olası kümelerin sayısı sayılarak birleştirilerek hesaplanan bir dizi ikili karar dizisi (her sayı kümenin içinde veya dışındadır) tarafından oluşturulur.

Büyük n için bellek kullanımı , bu binom bölümleme stratejisinin bir sürümü kullanılarak azaltılır .

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");
    }
}

Çevrimiçi deneyin!

(Not: TIO sürümünde birkaç değişiklik vardır. Birincisi, bellek limiti 1 GiB'ye düşürülür. İkincisi, TIO, a yazmanıza izin vermediği Cargo.tomlve gibi harici kasalara bağlı olmadığından rand, bunun yerine drand48C kütüphanesinden FFI. Tohumlamak için uğraşmadım, bu yüzden TIO sürümü her koşuda aynı sonucu verecektir. Resmi kıyaslama için TIO sürümünü kullanmayın.)


Kayan nokta biçimi sınırlı ln_add_expolduğundan, mutlak farkın ~ 15 ya da daha büyük olup olmadığını kontrol ederek optimize etmek mümkündür , bu tür bir çok ekleme varsa daha hızlı olabilir.
user202729

@ user202729 Hayır, neredeyse tüm ln_add_exparamalar karşılaştırılabilir girişler içeriyor.
Anders Kaseorg

3

Java 7+, TIO'da n = 50 inç ~ 30 saniye

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;
      }
    }
  }
}

Şimdilik bu zorluğun kod-golf versiyonu için cevabımın yorumlanmamış versiyonu, sadece küçük bir değişiklikle: aralıktaki bir tamsayı java.util.Random#nextInt(limit)yerine kullanılır , çünkü yaklaşık iki kat daha hızlıdır .(int)(Math.random()*limit)[0, n)

Çevrimiçi deneyin.

Açıklama:

Kullanılan yaklaşım:

Kod iki bölüme ayrılmıştır:

  1. nToplamı rastgele tamsayıların bir listesini oluşturun n squared.
  2. Daha sonra tüm değerlerin benzersiz olup olmadığını ve hiçbirinin sıfır olup olmadığını kontrol eder ve her ikisi de falsey ise, bir sonuç elde edene kadar durulayın ve tekrarlayın.

Adım 1 aşağıdaki alt adımlarla yapılır:

1) n-1Aralıkta rastgele tamsayıların bir miktarını oluşturun [0, n squared). Ve eklemek 0ve n squaredbu listeye. Bu O(n+1)performansta yapılır .
2) Daha sonra diziyi yerleşik ile sıralar java.util.Arrays.sort(int[]), Bu, O(n*log(n))dokümanlarda belirtildiği gibi performansta yapılır :

Belirtilen ints dizisini artan sayısal sıraya göre sıralar. Sıralama algoritması, Jon L. Bentley ve M. Douglas McIlroy'un "Bir Sıralama Fonksiyonu Mühendislik", Software-Practice and Experience, Vol. 23 (11) S. 1249-1265 (Kasım 1993). Bu algoritma, diğer hızlı sıralamaların ikinci dereceden performansa düşmesine neden olan birçok veri kümesinde n * log (n) performansı sunar.

3) Her çift arasındaki farkı hesaplayın. Bu sonuçta ortaya çıkan farklar listesi, ntoplanan tam sayıları içerecektir n squared. Bu O(n)performansta yapılır .

İşte bir örnek:

// 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

Bu nedenle yukarıdaki bu üç adım, adım 2'den ve her şeyin etrafındaki döngüden farklı olarak performans için oldukça iyidir, bu temel bir kaba kuvvettir. 2. Adım şu alt adımlara ayrılmıştır:

1) Farklar listesi zaten a java.util.Set. Bu Setin boyutunun eşit olup olmadığını kontrol edecektir n. Öyleyse, ürettiğimiz tüm rastgele değerlerin benzersiz olduğu anlamına gelir.
2) Ve aynı zamanda hiçbir içerdiğini kontrol edecektir 0meydan aralığında rastgele değerler sorar beri Set içinde [1, X]nerede Xolduğu n squaredeksi toplamı [1, ..., n-1]da belirttiği gibi, @Skidsdev aşağıda açıklamada.

Yukarıdaki iki seçenekten herhangi biri (tüm değerler benzersiz değilse veya bir sıfır mevcutsa), yeni bir dizi oluşturur ve 1. adıma sıfırlayarak tekrar ayarlayın. Bu, bir sonuç elde edene kadar devam eder. Bu nedenle, zaman biraz değişebilir. TIO için 3 saniyede bir kez n=50, ama aynı zamanda 55 saniyede bittiğini gördüm n=50.

Tekdüzelik kanıtlamak:

Bunun tamamen dürüst olduğunu nasıl kanıtlayacağımdan tam olarak emin değilim. java.util.Random#nextIntDocs tarif edildiği gibi, kesin üniforma:

intBu rasgele sayı üretecinin dizisinden eşit olarak dağıtılmış bir sonraki yalancı rayı döndürür . Genel sözleşmesi, nextIntbir intdeğerin sahte olarak üretilip iade edilmesidir. 2 32 olası intdeğerin tümü (yaklaşık) eşit olasılıkla üretilir.

Bu (sıralı) rastgele değerler arasındaki farklar elbette tekdüze değildir, ancak bir bütün olarak kümeler tekdüzedir. Yine, bunu matematiksel olarak nasıl kanıtlayacağımdan emin değilim, ancak burada,10,000n=10 çoğu kümenin benzersiz olduğu bir Sayaçlı bir Haritaya oluşturulan kümeleri (for ) koyacak bir komut dosyası ; bazıları iki kez tekrarlanır; ve tekrarlanan maksimum oluşum genellikle aralıktadır [4,8].

Kurulum Talimatları:

Java, Java kodunun nasıl oluşturulacağı ve çalıştırılacağı konusunda bol miktarda bilgiye sahip oldukça iyi bilinen bir dildir, bu kadar kısa tutacağım.
Kodumda kullanılan tüm araçlar Java 7'de mevcuttur (belki de zaten Java 5 veya 6'da, ancak her ihtimale karşı 7'yi kullanalım). Java 7 zaten arşivlenmiş eminim, bu yüzden kodumu çalıştırmak için Java 8 indirmenizi öneririm.

Gelişmelerle ilgili düşünceler:

Sıfır kontrolü için bir iyileştirme bulmak ve tüm değerlerin benzersiz olup olmadığını kontrol etmek istiyorum. 0Diziye eklediğimiz rastgele değerin zaten içinde olmadığından emin olarak daha önce kontrol edebilirdim , ancak birkaç şey anlamına gelir: dizi ArrayListyerleşik yöntemi kullanabilmemiz için bir olmalıdır .contains; henüz listede bulunmayan rastgele bir değer bulana kadar while döngüsü eklenmelidir. Sıfır için kontrol artık .contains(0)Sette yapıldığı için (sadece bir kez kontrol edilir), performansın .containsen az nkez kontrol edilecek olan Listedeki döngüyü eklemeye kıyasla, bu noktada kontrol etmesi daha olasıdır. , ama büyük olasılıkla daha fazla.

Benzersizlik kontrolüne gelince, sadece programın 1. adımından sonra toplanan nrastgele tamsayılarımız var n squared, bu yüzden ancak o zaman hepsinin benzersiz olup olmadığını kontrol edebiliriz. Dizi yerine sıralanabilir bir Liste tutmak ve aradaki farkları kontrol etmek mümkün olabilir, ancak ciddiye sadece onları koyarak Setve bu Setin boyutu bir nkez olup olmadığını kontrol daha performans artıracaktır şüpheliyim .


1
hıza yardımcı olursa, setteki hiçbir sayı, n^2 - sum(1..n-1)örneğin n=5en büyük geçerli sayı için daha büyük olamaz5^2 - sum(1, 2, 3, 4) == 25 - 10 == 15
Skidsdev

@ Skidsdev Teşekkürler, bunu düşünmemiştim. Mevcut yaklaşımımla bunu kullanamıyorum, çünkü rastgele değerler yerine rastgele çiftler arasındaki farkları elde ediyorum. Ama belki diğer cevaplar için de yararlı olabilir.
Kevin Cruijssen

1
Ortaya çıkan setin boyutu asla daha fazla olamaz n, değil mi? Bu durumda, 0kümeye ekleyebilir ve ardından boyutun (şimdi) daha fazla olduğunu kontrol edebilirsiniz n. Bu ancak farklılıkların tümü sıfırdan farklı ve farklıysa gerçekleşebilir.
Neil

@Neil Oh, bu oldukça akıllı ve kesinlikle birkaç bayt kapalı golf için benim kod golf cevap kullanacağım. Yine de buradaki performansı geliştirip geliştirmeyeceğinden emin değilim. HashSet.containsçoğu durumda yakındır O(1)ve en kötü durumda O(n)Java 7'de ve O(log n)Java 8+'da (zincirleme, çarpışma algılama ile değiştirildikten sonra geliştirilmiştir). Kümeyi 0çek için eklenenle birlikte iade etmeme izin verilirse , performans için gerçekten biraz daha iyi, ancak eğer if'i aramak zorunda kalırsam set.remove(0);, performansın biraz aynı olduğundan eminim.
Kevin Cruijssen

Ah, seti de geri döndürmen gerektiğini unuttum ... boş ver.
Neil

1

Mathematica n = 11

(While[Tr@(a=RandomSample[Range[#^2-#(#-1)/2],#])!=#^2];a)&     
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.