2

私はバットファイルを持っています

@echo %RANDOM%

コマンドラインを使用して実行します

start randomcheck.bat & start randomcheck.bat

2 つのコンソールが開かれ、どちらにも同じ番号の 4645 が含まれています。これは、異なる一時フォルダーを提供するというランダムの目的に失敗します(ランダム フォルダーは、それらを同時に使用する場合にのみ必要です)。バッチで通常の乱数発生器をどのように持っていますか?

更新 https://stackoverflow.com/a/19697361/1083704は、グローバル シード更新期間を定量化しました。Windows シェルのグローバル シードは毎秒更新されます。実際には、レースを防ぐために、安全マージンのためにもう 1 秒追加する必要があり、これが十分な手段であることを願っています。これは本当に最悪です。これは、iCore7 で 8 つのプロセスを起動するのに 16 秒かかることを意味します。そして、公式に何も指定されていないため、これが成功するかどうかはまだわかりません.8つのプロセスが最初に相対的な時間シフトで開始されるにもかかわらず、2つのプロセスが同時に終了する可能性があり、それらが同時に再起動しません。これは完全にでたらめです。私の質問は、C++ や VBScript に頼らずに、これをバッチで解決できるかということでした。

4

6 に答える 6

11

MC ND は、彼の回答内のすべての点で 100% 正しいです。

CMD.EXE の各インスタンスは、起動時に、現在の時刻から 1 秒の精度で取得されたシードを使用して、乱数ジェネレーターを初期化します。同じ秒内に起動するすべての CMD.EXE プロセスは、同一の乱数シーケンスを取得します。

追加の側面 - 連続する秒の初期乱数は非常にゆっくりと変化します。最初の乱数は、実際には時間から導出されたシード値である可能性があるように見えますが、よくわかりません。

編集 - 私は元々、実験を通じてこれらすべてを推測していました。しかし、それ以来、信頼できる情報源からの確認を見てきました。

CMD.EXE のシードが 1 秒に 1 回だけ変化し、シードが非常にゆっくりと変化する様子を示すスクリプトを次に示します。

@echo off
setlocal
set "last=%time:~9,1%"
for /l %%N in (1 1 30) do (
  call :wait
  cmd /c echo %%time%% %%random%% %%random%% %%random%% %%random%% %%random%% %%random%%
)
exit /b

:wait
if %time:~9,1% equ %last% goto :wait
set "last=%time:~9,1%"
exit /b

-- 出力 1 --

22:13:26.31 30024 16831 1561 8633 8959 14378
22:13:26.41 30024 16831 1561 8633 8959 14378
22:13:26.51 30024 16831 1561 8633 8959 14378
22:13:26.61 30024 16831 1561 8633 8959 14378
22:13:26.71 30024 16831 1561 8633 8959 14378
22:13:26.81 30024 16831 1561 8633 8959 14378
22:13:26.91 30024 16831 1561 8633 8959 14378
22:13:27.01 30027 27580 19425 32697 19274 18304
22:13:27.11 30027 27580 19425 32697 19274 18304
22:13:27.21 30027 27580 19425 32697 19274 18304
22:13:27.31 30027 27580 19425 32697 19274 18304
22:13:27.41 30027 27580 19425 32697 19274 18304
22:13:27.51 30027 27580 19425 32697 19274 18304
22:13:27.61 30027 27580 19425 32697 19274 18304
22:13:27.71 30027 27580 19425 32697 19274 18304
22:13:27.81 30027 27580 19425 32697 19274 18304
22:13:27.91 30027 27580 19425 32697 19274 18304
22:13:28.01 30030 5560 4521 23992 29588 22231
22:13:28.11 30030 5560 4521 23992 29588 22231
22:13:28.21 30030 5560 4521 23992 29588 22231
22:13:28.31 30030 5560 4521 23992 29588 22231
22:13:28.41 30030 5560 4521 23992 29588 22231
22:13:28.51 30030 5560 4521 23992 29588 22231
22:13:28.61 30030 5560 4521 23992 29588 22231
22:13:28.71 30030 5560 4521 23992 29588 22231
22:13:28.81 30030 5560 4521 23992 29588 22231
22:13:28.91 30030 5560 4521 23992 29588 22231
22:13:29.01 30033 16308 22385 15287 7135 26158
22:13:29.11 30033 16308 22385 15287 7135 26158
22:13:29.21 30033 16308 22385 15287 7135 26158

このスクリプトは、乱数ジェネレーターが単一の CMD.EXE プロセス内で "適切に" 動作することを示しています。

@echo off
setlocal enableDelayedExpansion
set "last=%time:~9,1%"
for /l %%N in (1 1 30) do (
  call :wait
  echo !time! !random! !random! !random! !random! !random! !random!
)
exit /b

:wait
if %time:~9,1% equ %last% goto :wait
set "last=%time:~9,1%"
exit /b

-- 出力 2 --

22:16:10.30 24175 26795 4467 2450 12031 9676
22:16:10.40 6873 17221 14201 17898 32541 29918
22:16:10.50 700 21044 25922 8616 24057 7657
22:16:10.60 25370 6519 26054 28443 4865 1931
22:16:10.70 26989 9396 12747 26808 6282 32182
22:16:10.80 22778 11460 11989 26055 10548 1809
22:16:10.90 4668 27372 30965 12923 5941 16533
22:16:11.00 23426 11396 24402 29658 5150 11183
22:16:11.10 1557 13572 18815 21801 4103 23119
22:16:11.20 3459 30126 20484 32750 3360 16811
22:16:11.30 14041 26960 31897 24736 16657 1954
22:16:11.40 5112 18377 30475 18837 12216 10237
22:16:11.50 13136 6241 27074 29398 8996 9738
22:16:11.60 16027 15122 13659 28897 4827 29753
22:16:11.70 27502 8271 11489 21888 16590 7886
22:16:11.80 30405 25582 7288 5432 7310 26557
22:16:11.90 202 11076 23205 20739 28053 12621
22:16:12.00 4234 20370 10355 5974 27590 8732
22:16:12.10 24411 21836 16161 24731 22898 10378
22:16:12.20 23060 17903 10788 19107 29825 15561
22:16:12.30 6772 1371 674 13257 15504 18422
22:16:12.40 1344 31971 23977 8630 10789 15367
22:16:12.50 18945 17823 20691 10497 5958 31613
22:16:12.60 18294 10398 26910 8744 21528 272
22:16:12.70 25603 9991 24084 11667 16977 5843
22:16:12.80 19405 5457 16285 11165 26783 10627
22:16:12.90 20041 31763 26390 11994 19285 12287
22:16:13.00 21342 13853 9336 24080 2555 2067
22:16:13.10 9328 30429 1722 2211 22934 24871
22:16:13.20 8168 21818 19125 11102 449 8813

最後に、このスクリプトは%random%、特定の行内の各行が独自の値に展開される方法を示していますが、ループ行は 1 回しか解析されないため、行の値はループの反復間で変化しません。

@echo off
setlocal
set "last=%time:~9,1%"
for /l %%N in (1 1 30) do (
  call :wait
  echo %time% %random% %random% %random% %random% %random% %random%
)
exit /b

:wait
if %time:~9,1% equ %last% goto :wait
set "last=%time:~9,1%"
exit /b

-- 出力 3 --

22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
22:20:10.98 28188 30311 32299 7392 5874 32157
于 2013-10-31T02:22:04.940 に答える
5

ハイブリッド バッチ Jscript ソリューション

@set @e=0 /*
 @echo off
   set @e=
   cscript //nologo //e:jscript "%~f0"
 exit /b
*/

function getRandomNumber(min, max) {
 return Math.floor(Math.random() * (max - min + 1)) + min;
}

WScript.echo(getRandomNumber(0, 10000));
于 2013-10-31T13:45:47.603 に答える
4

完全な乱数ジェネレーターでさえ、(最終的には) 衝突を生成する可能性があります。堅牢なソリューションでは、衝突が発生する可能性があると想定し、それに応じて補償する必要があります。

以下は、私が過去に成功裏に使用した戦略の 1 つです。

スクリプトの先頭に一意の一時ファイル名を割り当てます。%TIME%疑似「乱数」の値を使用します。:文字列をファイル名として有効にするには、 を何も置き換えません。2 つのプロセスは、互いに 1/100 秒以内に開始された場合にのみ衝突します (プロセスが 1 日以上実行されないと仮定します)。

衝突する可能性があります。衝突は、一時的なロック ファイルを介して検出できます。スクリプトの本体をサブルーチンに配置し、「乱数」を使用してロック ファイルにリダイレクトされた非標準のファイル ハンドルを使用してメイン ルーチンを呼び出します。一度に 1 つのプロセスのみが出力をロック ファイルにリダイレクトできます。ロックが検出された場合は、単純にループバックして再試行してください。

@echo off
setlocal

:getUnique

:: Derive a pseudo "unique" name from script name and current time
set "tempBase=%temp%\%~nx0.%time::=%"

:: If you want to test the lock mechanism, uncomment the following
:: line which removes the time component from the "unique" name
::set "tempBase=%temp%\%~nx0.notUnique"

:: Save stderr, then redirect stderr to null
3>&2 2>nul (

  %= Establish lock =%
  9>"%tempBase%.lock" (

    %= Restore stderr and call main routine =%
    2>&3 (call :main %*)

    %= Capture the returned errorlevel if necessary =%
    call set "err=%%errorlevel%%

    %= Force ERRORLEVEL to 0 so that any error detected outside =%
    %= this block must be due to lock failure                   =%
    (call )

  %= Loop back and try again if lock failed due to collision =%
  ) || goto :getUnique
)

:: Delete the temp files and exit with the saved errorlevel
del "%tempBase%*"
exit /b %err%


:main
:: The rest of the script goes here.
:: Additional unique temp file names can be derived from %tempBase% as needed.

:: For this demo, I'll just list the temp file(s) and pause
dir /b "%tempBase%*"
pause

:: Exit with an error for testing purposes
exit /b 1

2 つのプロセスが同じ一意の名前を取得することはほとんどありませんが、その場合、2 番目のプロセスが衝突を検出し、ループバックして、成功するまで再試行します。

ロックをテストする場合は、nonUnique tempBase 行のコメントを外します。2 つのコンソール ウィンドウを開き、両方のウィンドウでスクリプトを起動します。1 つ目は、正常にメイン ルーチンに入り、一時停止します。2 番目はループし、最初の処理が終了するのを待ちます。1 番目のキーを押すと、1 番目は即座に終了し、2 番目はメイン ルーチンに続きます。

1/100 秒を超える精度が必要な場合、またはプロセスが 1 日以上実行される可能性がある場合は、 を使用WMIC OS GET LOCALDATETIMEして日付と時刻を含む文字列を 1/1000 秒に取得することを検討する必要があります。

于 2013-11-13T00:31:09.530 に答える
2

1 秒でも 8 つのタスクを開始でき、それぞれが独自のランダム値を取得します。

乱数はメインタスクによって生成され、パラメーターとして送信されます。

setlocal EnableDelayedExpansion
for /L %%n in (1 1 8) DO start task.bat !random!

task.bat で独立したランダム ジェネレーターも必要な場合は、パラメーターをシード プレフィックスのように使用できます。

task.bat

setlocal EnableDelayedExpansion
set seed=%1
for /L %%n in ( 1 1 %seed%) do set dummy=!random!
于 2013-10-31T10:20:42.027 に答える