Come generare numeri casuali senza creare nuovi oggetti casuali?

5

Lo sto utilizzando come parte di un gioco, ma non è una questione di sviluppo del gioco, quindi lo sto mettendo in questo Stack Exchange più generale.

L'obiettivo è generare uscite "casuali" per un input intero fisso, ma (e questo è il copertoncino) per generare lo stesso output casuale ogni volta che stesso l'inserimento casuale è inserito.

L'idea è che la funzione genererà il mondo allo stesso modo ogni volta, quindi non abbiamo bisogno di memorizzare nulla; la funzione stessa è la memoria. Sfortunatamente la velocità di accesso è un po 'lenta, perché l'unico modo che posso trovare per generare numeri casuali è creare un nuovo oggetto Random () con un seme basato sull'input, che è sorprendentemente lento.

C'è un modo migliore? Non sono preoccupato per la generazione crittogena; in effetti selezionerò un seme casuale in anticipo e lo esporrò pubblicamente.

Il codice corrente ha il seguente aspetto:

private const int seed;

public MapCell GetMapCell(int x, int y)
{
    Random ran = new Random(seed + (x ^ y));
    return new MapCell(ran.NextInt(0, 4));
}

Dove MapCell è uno dei quattro tipi (in realtà è più complicato di questo, ma non molto). Il punto è che questo può essere chiamato per qualsiasi parametro, in qualsiasi momento, senza un ordine particolare, ma deve restituire sempre la stessa risposta, se xey sono sempre le stesse. Ecco perché non riesco a correggere un certo oggetto casuale e a usarlo ripetutamente.

Inoltre non voglio memorizzare nulla, perché voglio mantenere l'utilizzo della RAM piuttosto basso, ma consentire al giocatore di vagare liberamente ai bordi di Int.MaxValue

    
posta Richard Rast 17.08.2012 - 18:39
fonte

4 risposte

3

Sign è su una buona pista, ma il suo algoritmo è sbagliato. Non è molto casuale. In realtà è piuttosto difficile creare random come questo. Stavo giocando con questo e tutto ciò che ho provato creava modelli ovvi quando stampavo in 2D. Alla fine ho creato un algoritmo che non crea schemi visibili agli occhi. Ho cercato l'ispirazione negli algoritmi casuali esistenti.

public static uint bitRotate(uint x)
{
    const int bits = 16;
    return (x << bits) | (x >> (32 - bits));
}

public static uint getXYNoise(int x, int y)
{
    UInt32 num = seed;
    for (uint i = 0; i < 16; i++)
    {
        num = num * 541 + (uint)x;
        num = bitRotate(num);
        num = num * 809 + (uint)y;
        num = bitRotate(num);
        num = num * 673 + (uint)i;
        num = bitRotate(num);
    }
    return num % 4;
}

Quando questo algoritmo viene utilizzato per eseguire il rendering di 4 tonalità di immagini grigie, crea questo:

Perconfronto,l'algoritmocasualecreaquestomodello:

Anche l'algoritmo di Sign ha schemi:

    
risposta data 12.02.2014 - 23:33
fonte
3

Perché non combinare solo i due numeri e cancellarli? qualcosa come

private const int seed;

public MapCell GetMapCell(int x, int y)
{
    int combined = seed + (x ^ y);
    return new MapCell(combined.GetHashCode() %5);
}

Vuoi semplicemente mappare ogni coordinata X, Y ad una MapCell.

    
risposta data 17.08.2012 - 20:33
fonte
3

Nella programmazione The Art of Computer, volume 2 c'è una sezione dedicata ai numeri casuali. Potresti riuscire a trovare quello che stai cercando lì dentro.

Project Euler utilizza il seguente generatore di numeri casuali psuedo in alcuni dei suoi problemi (252 e 375 sono quelli che ho individuato per primi):

  S(0)   = 290797 
  S(n+1) = (S(n))^2 mod 50515093

Ovviamente, questo non ti dà una passeggiata fino a maxint, ma ti dà un approccio al tuo che richiede solo di salvare l'ultimo risultato. Se lavori con long invece di int, lascerebbe il lavoro (per quello che vale, 2 ^ 32-5 = 0xFFFFFFFB = 4.294.967.291 è il più grande 32 bit primo).

Nel talk rand () Considerato Nocivo il presentatore supera un numero di diverse opzioni per fare numeri casuali. Mentre parla di C ++, fornisce informazioni che possono essere utilizzate per trovare il metodo giusto in altre lingue.

In particolare, per le distribuzioni di numeri casuali uniformi ben note la cosa che vuoi trovare è mt19937 che sta per Mersenne twister con parametri molto particolari - si basa su 2 19937 - 1. (~ 11m nel talk).

La chiave con il twister Mersenne è che si tratta di numeri pseudo-casuali di altissima qualità. Se fai un po 'di giro, puoi trovarne le implementazioni in un numero di lingue . sorgente C originale

In modo specifico, per C # puoi usare Jon Skeet 's StaticRandom per ottenere numeri casuali in modo thread-safe - nessuna istanziazione di nuovi oggetti necessari ( codice ). Una modifica del codice dovrebbe consentire di passare un seme.

    
risposta data 17.08.2012 - 20:13
fonte
-1

Come commentato da Kyralessa, la classe Random dovrebbe essere creata e la sequenza richiesta da una istanza. Ciò consente di risparmiare tempo per la creazione di una nuova istanza di classe.

//fixed seed of 123
private Random ran = new Random(123);
public MapCell GetMapCell(int x, int y)
{
    //x and y should not be zero
    int r = ran.Next(0,x*y*4)%4;
    return new MapCell(r);
}
    
risposta data 24.08.2016 - 22:35
fonte

Leggi altre domande sui tag