Mit batch Random auswahl?

5 Antworten

Mit %RANDOM% kann man rechnen (Pseudozufallszahl zwischen 0 und 32767 einschließlich).

Karten aus einem Kartenstapel zu entfernen ist nicht ganz einfach, aber auch mit Batch machbar.

(Für "komplett random" braucht man einen Hardware-Zufallsgenerator, der z. B. mit Quantenrauschen arbeitet.)

Woher ich das weiß:Berufserfahrung – Software-Entwickler

ich habe mal eine kleine Demo zusammengeschreibselt:

@echo off
setlocal enabledelayedexpansion
set "cols=Kreuz,Pik,Herz,Karo"
set "val=2,3,4,5,6,7,8,9,10,Bube,Dame,Koenig,Ass"
set "cardNum=0"
call :initCardsTabel
  rem debug Zeile 
set "card"  & echo:


  rem  ziehe 1 Karte
call :drawCard
echo %drawnCard%
echo:

  rem test ziehe 6 karten  ....aber  niemals  mehr als Anzahl der Karten-1  (51)...
  rem ...sonst   bleibt :redraw in einer Endlosschleife  hängen
  for /l %%k in (1,1,6) do (
  call :drawCard
  echo Karte %%k = !drawnCard!
)  

pause
exit /b 



:initCardsTabel
  rem kartenstapel initialisieren
for %%a in (%cols%) do (
    for %%b in (%val%) do (
          rem  erhöhe Zähler cardnum
        set /a "cardnum+=1"
          rem Kartenwerte  in einem Array speichern
        set "card[!cardNum!]=%%a %%b
)   )
exit /b

:drawCard
  rem ziehe eine karte
  
  rem zufällige Nummer aus Anzahl  der Karten
  rem random modulo cardNum +1
:redraw
  rem  eine  neue Zufallszahl generieren ist  einfacher als den  ganzen Stapel jedesmal neu zu nummerieren
set /a "rNum=%RANDOM% %% cardNum +1"
  rem wenn Karte nicht mehr im Stapel -> neue Zufallszahl
if "!card[%rNum%]!"=="" goto :redraw
  rem Ergebnis  gezogene Karte  in variable %drawnCard% zurückgeben
set "drawnCard=!card[%rNum%]!
  rem gezogene Karte im Stapel löschen
set "card[%rNum%]="
exit /b


  • :initCardsTabel kombiniert die Kartenfarbe cols mit dem Kartenwert val und speichert diese in einem Array
  • cardNum gibt zählt automatisch die Karten
  • :drawCard ist das Herzstück und gibt in der Variable %dawnCard% farbe und wert einer Karte zurück

die Zufallszahl rNum ergibt sich aus %Random% modulo "Anzahl deer Karten" +1

Das ziehen bewirkt das die Karte nicht mehr im Stapel ist.

In einer höheren Programmiersprache hätte ich wahrscheinlich das Array neu nummeriert.
In Batch ist das zu langsam.

..also frage ich ob die Karte[ZufallsZahl] noch im Stapel ist...

wenn nicht versuche ich eine neue Zahl...

Je mehr Karten man vom Stapel Zieht um so wahrscheinlicher ist es auf eine "tote" Karte zu Treffen .
...aber selbst bei 51 gezogenen Karten ist die Verzögerung (durch das ermitteln einer neuen Zahl) kaum merklich.

Anmerkung zum Kartenstapel:

Der ist nicht gemischt ! ...warum auch ?...wenn wir ohnehin Zufällig wählen.

Vor längerer Zeit habe ich mal %random% getestet. bei 100000 Würfen waren sowohl gleichverteilung der Zahlen, wie auch aufeinanderfolge durchaus akzeptabel .

Um einen bessern Zufallswert zu bekommen kann man ja mehrere Würfe verknüpfen oder mit VBS/Javascript "würfeln"

http://www.robvanderwoude.com/vbstech_data_random.php

die mit Rem debug Zeile gekennzeichnete nachfolgende Zeile kannst Du löschen!

Woher ich das weiß:eigene Erfahrung – Ich mach das seit 30 Jahren

Erzesel  11.06.2018, 18:05

update:

prüft vor der dem Ziehen ob überhaupt noch Karten im Stapel sind verhindert Endlosschleife beim "überziehen":

:drawCard
  rem ziehe eine karte

:redraw
  rem prüfe ob noch Karten im Stapel
set "card[" >nul 2>&1 || ( 
      rem wenn keine Karten  mehr  im Stapel Fehlermeldung in Variable %drawnCard%
    set "drawnCard=Keine Karte uebrig..."
      rem ...und  Subroutine  mit %errorlevel%=1 abbrechen
    exit /b 1) 
set /a "rNum=%RANDOM% %% cardNum +1"
if "!card[%rNum%]!"=="" goto :redraw 
set "drawnCard=!card[%rNum%]!
set "card[%rNum%]="
exit /b



da Errorlevel einen Fehler meldet, kann man nach Call darauf reagieren.

for /l %%k in (1,1,100) do (
    call :drawCard && (
        echo Karte %%k = !drawnCard!
        rem diverses  anderes  was  sonst  noch  mit der gezogenen Karte  passieren soll
)   ) 

Einen Batch for /l -loop kann man zwar nicht abbrechen, aber man kann dafür sorgen das nur wenn kein Fehler vorhanden ist etwas passiert...

https://ss64.com/nt/syntax-redirection.html

 

0

Hier ein kleines Standardscript für Zufallszahlen: (min und max sind eingeschlossen)

SET MIN=1
SET MAX=6
SET /a r=MIN+(MAX-MIN+1)*%random%/32768

Da dein MIN bei den Karten wahrscheinlich immer 0 ist, geht es so noch einfacher:

SET anzahl=5 &rem Die Anzahl deiner Karten
SET /a index= (anzahl * %random%) /32768

Theoretisch erzeugst du dabei mit %random%/32768 eine zufällige Zahl zwischen 0 und 1 und multiplizierst sie mit deinem Maximalwert+1. Da Batch aber keine Kommazahlen unterstützt, muss man als letztes Teilen, sodass erst dort (ab!)gerundet wird.

Hier ein Beispiel um ein zufälliges Element aus einem Array zu bekommen:

For /L %%i in (0 1 10) do set "Karte[%%i]=example#%%i" & set /a "Karte.length=%%i+1"
SET /a index=(%Karte.length% * %random%) /32768
call echo Karte[%index%] = %%Karte[%index%]%%

~Tim


Erzesel  17.06.2018, 11:04

bei Modulo muss man nicht wissen welche Spanne %Random% hat

set /a "Zufallszahl=%RANDOM% %% gesamtAugen + minAugen "

0
timlg07  17.06.2018, 11:16
@Erzesel

Ich hatte nur mal gelesen, dass mit modulo die Wahrscheinlichkeiten ungleichmäßiger verteilt sind. Rechne ich mal nach

0
timlg07  17.06.2018, 11:22
@timlg07
You can control the number's range with:
set /a num=%random% %%100
- will produce number between 0~99.

-----------------------------------------------------------------------------------

Note that this will not be uniformly distributed! Taking the 0~99 example, the numbers 0~67 will occur slightly more often than the numbers 68~99 because 32767 modulo 100 is 67 and not 0 as it would have to be for a uniform distribution.
0
Erzesel  17.06.2018, 12:12
@timlg07

ich habe mal auf die schnelle einen kleinen Test geschrieben .

@echo off
setlocal enabledelayedexpansion
set "Augen=6"
for %%r in (8,12,23,153,2777,2778,15867,32000) do (

set /a "zufallTim=(Augen * %%r) /32768 +1"
set /a "zufallModulo= %%r %% Augen +1"
echo PseudoZufallszahl= %%r
echo AugenTim=!zufallTim!   , AugenModulo=!zufallModulo!

)
pause
exit /b 

%%r ersetzt Random um eine kontrollierte Umgebung zu gewärleisten.

Das problem bei Deiner Methode ist, das alle Zufallszahlen unter Augen*random =32769 0 produzieren (1 in der Demo). Das bedeutet ein ein aufeinanderfolgender Anteil von Ergebnissen (32768 /Augen) ist von vornherein erst einmal 0! (nicht gut für die Gleichverteilung)
Du kannst ja die ZahlenListe erweitern und .

1
timlg07  17.06.2018, 12:19
@Erzesel

Ich habe mal die Methode mit Modulo ein paar mal durchlaufen lassen (hab aus Gewohnheit mal eine Million Durchläufe genommen, aber Batch braucht da etwas länger und ist immer noch am rechnen)

Der derzeitige Zwischenstand:

#1 = 33615
#2 = 33521
#3 = 33849
#4 = 33804
#5 = 33362
#6 = 33869
#7 = 33611
#8 = 33610
#9 = 33772
#10= 33737

Sieht eigentlich ganz gut aus, nur Rest 5 kommt eher seltener vor.

0
Erzesel  17.06.2018, 12:23
@Erzesel

ps: meine Batch nochmal verbessert:

@echo off & rem https://www.gutefrage.net/frage/mit-batch-random-auswahl?foundIn=list-answers-by-user#comment-182708070
setlocal enabledelayedexpansion
for /F "delims=;" %%a in ('"prompt $H; & for %%. in (nix) do call "') do (set "BS=%%a")
set "cols=Kreuz,Pik,Herz,Karo"
set "val=2,3,4,5,6,7,8,9,10,Bube,Dame,Koenig,Ass"
set "cardNum=0"
echo Mische Karten
call :initCardStack 5
  rem debug Zeile 
::set "card"  & echo:

timeout 1 >nul
cls


 
for /l %%k in (1,1,18) do (
    call :drawCard card && (
        echo Karte %%k = !drawnCard!
        rem diverses  anderes  was  sonst  noch  mit den Karten  passieren soll
)   )
pause
exit /b 



:initCardStack
  rem params:  opt. %1 Mixcount
  rem kartenstapel initialisieren
for %%a in (%cols%) do (
    for %%b in (%val%) do (
          rem  erhöhe Zähler cardnum
        set /a "cardnum+=1"
          rem Kartenwerte  in einem Array speichern
        set "card[!cardNum!]=%%a %%b
)   )
for /l %%. in (1,1,%~1) do call :mixCards

exit /b

:mixCards
for /l %%k in (1,1,%cardNum%) do (
    call :drawCard card && (
        set "tmpCardsStack[%%k]=!drawnCard!"
        <nul set /p "=H"
)   )
for /l %%k in (1,1,%cardNum%) do (
    call :drawCard tmpCardsStack && (
        set "card[%%k]=!drawnCard!"
        <nul set /p "=%BS%"
)   )
exit /b

:drawCard
  rem params: %1 name des Kartenstapels
  rem ziehe eine karte   
  rem prüfe ob noch Karten im Stapel
set "%~1[" >nul 2>&1 || ( 
      rem wenn keine Karten  mehr  im Stapel Fehlermeldung in Variable %drawnCard%
    set "drawnCard=Keine Karte uebrig..."
      rem ...und  Subroutine  mit %errorlevel%=1 abbrechen
    exit /b 1) 
:redraw    
set /a "rNum=%RANDOM% %% cardNum +1"
if "!%~1[%rNum%]!"=="" goto :redraw 
set "drawnCard=!%~1[%rNum%]!
set "%~1[%rNum%]="
exit /b

Mischt jetzt die Karten:

call :initCardStack 5 5mal

Habe schon überlegt ob ich an langen Winterabenden Knack oder gar MAUMau in Batch schreibe... ;)

0
Erzesel  17.06.2018, 12:34
@Erzesel

ps versuche mal bei der vergleichsdemo 32000,32001,32002,32004,32005,32006 und erschrecke nicht, es wird noch schlimmer als nur 0 ... von Gleichverteilung keine Spur :p

0
timlg07  17.06.2018, 13:51
@Erzesel

Naja Gleichverteilung heißt ja nicht zwingend das in jedem beliebigen Intervall jede Zahl gleich oft vorkommen muss. Die Methoden arbeiten halt unterschiedlich.

Wie schon in meiner Antwort erklärt erzeugt man mithilfe von %random%/32768 eine Zahl zwischen 0 und 1 (wie zB. auch mit Math.random() in JS). Das Intervall [0;1] wird mithilfe der Variablen min & max auf das Intervall [min;max] gebracht.

Ist %random% groß (zB. irgendwas um die 32000), so ist auch %random%/32768 groß (zB. 0.9765625 für 32000) und damit auch das Ergebnis =6.

ergebnis ∼ random (direkt proportional)

Bei modulo hingegen beginnt alle 6 Zahlen die Zählung von vorne, es ist egal bei welchem Vielfachem man sich befindet.

12%6=0; 13%6=1; 14%6=2; ...
18%6=0; 19%6=1; ...
...
36%6=0; 37%6=1; ...

0
set /a Zufallszahl=%RANDOM%

LitegameZ 
Beitragsersteller
 09.06.2018, 19:45

@echo off

set /a lll=%RANDOM%

pause

???

0
LitegameZ 
Beitragsersteller
 09.06.2018, 20:07
@Roderic

Da kommt nur: Drücken sie eine bieliebige um fortzuufahren oder so

0

@echo off
:debut
set /a Zufallszahl=%RANDOM%
echo %Zufallszahl%
pause
goto debut