Board logo

标题: [其他] 【已解决】BAT:多次随机组合不重复的字符,如何控制各字符总数均等 [打印本页]

作者: 思想之翼    时间: 2023-9-2 21:23     标题: 【已解决】BAT:多次随机组合不重复的字符,如何控制各字符总数均等

本帖最后由 思想之翼 于 2023-9-3 16:29 编辑

从 00-99 中,随机抽取50个不重复的字符,组成1行,每行50列。
随机1000次,组成1000行,每行50列,共有50000个字符。
在这50000个字符中,若想控制 00 01 02...97 98 99 的每个字符,出现个数均为500个,BAT代码能否实现?

例如:
00 01 02 03...48 49
00 01 02 03...49 50
...
50 51 52 53...98 99

在上述行列中,00共有500个,01共有500个 ... 99共有500个。
作者: 77七    时间: 2023-9-3 03:13

本帖最后由 77七 于 2023-9-3 03:35 编辑
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set m=100000
  4. %1 (for /f "tokens=2 delims==" %%a in ('%0 rem^|sort') do ((set /p=%%a <nul)&(set /a n+=1,i=n %% 50)&(if !i! equ 0 (echo=))))>1.txt&pause&exit
  5. for /l %%k in (100,1,199) do (
  6.         set str=%%k
  7.         for /l %%l in (1,1,500) do (
  8.                 set /a m+=1
  9.                 echo !random!!m:~-6!=!str:~-2!
  10.         )
  11. )
复制代码

需要30秒
作者: Five66    时间: 2023-9-3 04:12

本帖最后由 Five66 于 2023-9-3 17:56 编辑

速度慢而且不准确,已编辑
作者: 思想之翼    时间: 2023-9-3 10:45

本帖最后由 思想之翼 于 2023-9-3 12:33 编辑

回复 3# Five66
感谢!测试结果正确。如何将结果输出到文本?
作者: 思想之翼    时间: 2023-9-3 10:49

本帖最后由 思想之翼 于 2023-9-3 12:32 编辑

回复 2# 77七
感谢!经测试,从 00-99 中,随机抽取50个不重复的字符组成1行,其中有相同的字符。
比如:
36 49 44 58 44 21...49 50
作者: hfxiang    时间: 2023-9-3 12:49

本帖最后由 hfxiang 于 2023-9-3 13:44 编辑

回复 5# 思想之翼
出错了(运算到997行,第27个数后,开始严重耗时,无法忍受),已删除,抱歉
作者: 77七    时间: 2023-9-3 12:52

回复 6# hfxiang


   楼主要求每行不重复
作者: hfxiang    时间: 2023-9-3 12:55

本帖最后由 hfxiang 于 2023-9-3 13:45 编辑

回复 7# 77七

出错,已删除
作者: 77七    时间: 2023-9-3 12:58

回复 8# hfxiang

观察下最后一行,测试两次,最后一行比较明显
就是每行内的数字不能重复
  
  1. 60 38 38 05 40 38 33 38 12 12 12 64 60 33 60 60 33 12 33 40 33 33 33 40 60 40 12 12 40 33 33 33 33 64 33 64 64 64 40 40 40 64 40 40 40 64 64 64 64 64
复制代码

作者: hfxiang    时间: 2023-9-3 13:00

回复 9# 77七
哦,俺得好好再看看
作者: 思想之翼    时间: 2023-9-3 13:55

回复 6# hfxiang

辛苦了,感谢!
作者: Five66    时间: 2023-9-3 14:21

回复 4# 思想之翼


    第49行后面加上重定向的文件就行了
比如将第49行改成:echo;%oput% >>aaaaaaaaaaa.txt
就是输出到当前目录的aaaaaaaaaaa.txt中
还有要注意的:
输出非常非常非常慢(我的渣电脑粗略计算至少要1小时,2秒多1行,越到后面越慢)
输出的文件每行开头有个空格
多次运行时,请将之前输出的文件的删除或改名或移动,不然输出结果会跟之前混在一起(因为同名)
作者: Five66    时间: 2023-9-3 14:33

本帖最后由 Five66 于 2023-9-3 15:08 编辑

回复 4# 思想之翼


    中断执行后发现某些变量变0,试了很多次都复现不能,感觉像是刚好在代码执行赋值那里中断才导致变量变0的,实际代码应该没问题
作者: 思想之翼    时间: 2023-9-3 15:23

回复 13# Five66
感谢!
1.代码在cmd界面运行,停止后,复制到文本,只有898行。
2.>a.txt,只能写入1行就停止了。
3.随机的每行字符不重复,但是1000行整体的字符,不能满足00=500个 01=500个...99=500个
作者: Five66    时间: 2023-9-3 15:54

回复 14# 思想之翼



    1 应该是显示的行数达到cmd窗口界面的上限了,之前的被覆盖了
    2 重定向 >a.txt 是在那里用的?代码里的话必须用 >>a.txt 只有在cmd界面里才能用,不过代码有pause,cmd里界面用的话可能不准确
    3 这个有点难搞,举几个例子吧,比如那些数字不足,我再看看
作者: WHY    时间: 2023-9-3 16:44

Test.js
  1. var arr = [];
  2. var tmp = [];
  3. var fso = new ActiveXObject('Scripting.FileSystemObject');
  4. for(var i=100; i<=199; i++) tmp.push( (''+i).substr(1) );
  5. for(var i=1; i<=500; i++){
  6.     tmp.sort(function(){return Math.random()>0.5 ? 1 : -1});
  7.     var str = tmp.join(' ');
  8.     str = str.replace(/^((?:\d\d ){49}\d\d) /, '$1\r\n');
  9.     arr.push(str);
  10. }
  11. fso.OpenTextFile('1.txt', 2, true).Write(arr.join('\r\n'));
  12. WSH.Echo('Done');
复制代码

作者: WHY    时间: 2023-9-3 16:46

Test.ps1
  1. $arr = (100..199) -replace '^1';
  2. $out = [Collections.ArrayList]@();
  3. for ($i=1; $i -le 500; $i++) {
  4.     $arr = Get-Random $arr -Count $arr.Count;
  5.     [void]$out.Add($arr[0..49] -join ' ');
  6.     [void]$out.Add($arr[50..99] -join ' ');
  7. }
  8. [IO.File]::WriteAllLines('1.txt', $out);
复制代码

作者: pd1    时间: 2023-9-3 17:29

回复 17# WHY


    这样是不是有点不随机了  比如任意一个值在第一行出现后就不可能在第二行出现。
作者: Five66    时间: 2023-9-3 17:49

额,确实像这样排好再打乱简单方便快速准确,不过输出顺序应该再打乱一次
作者: WHY    时间: 2023-9-3 18:18

回复 18# pd1


    是的,如果一个数(比如01)第一行、第二行都出现,有可能总数会超过500个,不符合题意。
如果有必要,可以在第10行上面插入一行:
  1. $out = Get-Random $out -Count $out.Count
复制代码

作者: 77七    时间: 2023-9-3 19:13

本帖最后由 77七 于 2023-9-3 20:35 编辑

下午用定义行号和数字也写了个,但是在某一行,应该是出现有51个数已经用了500次...总是陷入死循环
WHY大佬直接将00-99一组分为两行,非常巧妙。
用批处理参考此思路也写了个,打乱排序需要2分钟,效率还是不行...,不打乱排序快点,
优化了下,打乱排序17秒
  1. @echo off
  2. %1 (for /f "tokens=2 delims==" %%a in ('%0 rem^|sort') do (echo %%a))>1.txt&pause&exit
  3. for /l %%l in (1001,1,1500) do (
  4. setlocal enabledelayedexpansion
  5. for /l %%k in (100,1,199) do (
  6. set str=%%k
  7. set #!random!#%%k=!str:~-2!
  8. )
  9. for /f "tokens=2 delims==" %%a in ('set #') do (
  10. set str2=%%a !str2!
  11. )
  12. echo !random!#%%l1=!str2:~0,149!
  13. echo !random!#%%l2=!str2:~150,149!
  14. endlocal
  15. )
复制代码

作者: qixiaobin0715    时间: 2023-9-4 09:32

和楼上的基本相似,理了理思路,这样代码更易懂:
  1. @echo off
  2. (for /l %%i in (1,1,500) do (
  3.     setlocal enabledelayedexpansion
  4.     for /l %%j in (0,1,99) do (
  5.         set /a n+=1
  6.         if %%j lss 10 (
  7.             set _!random!-!n!=0%%j
  8.         ) else (
  9.             set _!random!-!n!=%%j
  10.         )
  11.     )
  12.     for /f "tokens=2 delims==" %%k in ('set _') do (
  13.         set /a m+=1
  14.         if !m! leq 50 (
  15.             set str1=!str1! %%k
  16.         ) else (
  17.             set str2=!str2! %%k
  18.         )
  19.     )
  20.     echo,!str1!
  21.     echo,!str2!
  22.     endlocal
  23. ))>1.txt
  24. pause
复制代码

作者: pd1    时间: 2023-9-4 10:29

回复 20# WHY
所以我感觉这个是一个算法题,需要判断如果超过500个了就不再使用它了,还得考虑的一个点是500-任意一个值已经使用过的次数不能大于剩余生成的行数。
不过既然楼主对数据没有这方面的需求,你的这个方法还是比较巧妙的。
作者: qixiaobin0715    时间: 2023-9-4 11:34

回复 23# pd1
是算术题,1002行重复个数就是501,2000行个数就是1000。
作者: qixiaobin0715    时间: 2023-9-4 13:13

本帖最后由 qixiaobin0715 于 2023-9-4 13:15 编辑

回复 18# pd1
我觉得WHY的思路没有问题。楼主的题目要求决定了只要出现一行随机数,必定要跟随互补的另一行。可以自己手工排排看,按最简单4行或6行,每个数出现2次或3次。只不过代码输出的是:单数行紧跟一偶数行,为互补行,给人表面的感觉不是随机的,实际上互补行是必须出现的。如果按照楼主的要求,最终数据都是两两互补,没有意外,只是出现的位置可以不同而已。
作者: pd1    时间: 2023-9-4 14:23

回复 25# qixiaobin0715
我也说了思路没问题啊,只是没那么随机,可能如果要生成的数据量足够多才能必然出现互补行吧,虽然我没计算过到底需要多少行才出现,我也不会算。

    举个小例子,0-9  每个出现2次
01234
56789
03579
12468

上面是生产的12互补,34互补
把2行的7和4行的4换一下,有没有互补行呢
01234
56489
03579
12768

所以你是如何得出必有互补行的
作者: qixiaobin0715    时间: 2023-9-4 14:35

回复 26# pd1
唐突了,是我自己考虑欠妥。
作者: buyiyang    时间: 2023-9-4 14:36

本帖最后由 buyiyang 于 2023-9-4 14:38 编辑

回复 23# pd1


    这个算法题还是有点复杂,我一开始只判断某数使用次数达500次就不再使用,结果到980多行时就没有50个数可使用了(比如999行时,75个数使用了500次,25个数使用了498次),我实在没有什么好的办法解决这个问题。你说的“500-任意一个值已经使用过的次数不能大于剩余生成的行数”也不行
作者: buyiyang    时间: 2023-9-4 14:40

本帖最后由 buyiyang 于 2023-9-4 14:43 编辑

回复 26# pd1


    0-9随机10行每个数出现5次就会互补。
作者: qixiaobin0715    时间: 2023-9-4 14:45

回复 23# pd1
又看了看,开始回复23楼的帖子也是文不对题,没有理解你所说的意思,抱歉。
作者: pd1    时间: 2023-9-4 14:54

回复 28# buyiyang


    我试着py写,和你遇到一样的问题,我是把50000个数放一起,转set去重然后取50个  然后把这50个数从5万个数里剔除掉  然后再循环  也是到970-980左右 出现剩下的数字重复了取不到50个了
所以还是得设计算法来处理,我就水平有限,搞不定了
作者: pd1    时间: 2023-9-4 14:57

回复 30# qixiaobin0715


    大家互相交流是好事,能学到自己想要的都是好事。采用互补的2行的这种很巧妙,而且运行很快,生成的数据也能用。我是没想到这种方式
作者: 77七    时间: 2023-9-4 18:34

如果对打乱排序后的结果,再处理,比如以两行为单位,随机排序这100个”数字“,每行不重复,让结果更随机一些,还挺有意思...感觉还是稍微有点难度...
  1. 35 70 39 96 45 26 79 86 40 72 92 46 31 68 95 71 65 73 58 90 29 89 10 82 50 42 94 84 01 34 17 93 06 32 74 07 44 76 37 08 55 47 53 22 23 88 13 16 66 19
  2. 55 02 23 30 70 56 53 42 25 33 48 29 79 09 66 98 24 80 11 94 93 13 61 73 40 43 68 67 51 76 26 37 19 59 35 75 95 96 90 63 22 64 69 97 41 15 54 77 86 83
复制代码


我抽取了结果的两行(不是互补的),在写,还没有写出来...
作者: 77七    时间: 2023-9-4 19:35

本帖最后由 77七 于 2023-9-5 13:58 编辑

回复 33# 77七


   我写了个比较复杂的...
  1. @echo off
  2. >2.txt echo 35 70 39 96 45 26 79 86 40 72 92 46 31 68 95 71 65 73 58 90 29 89 10 82 50 42 94 84 01 34 17 93 06 32 74 07 44 76 37 08 55 47 53 22 23 88 13 16 66 19
  3. >>2.txt echo 55 02 23 30 70 56 53 42 25 33 48 29 79 09 66 98 24 80 11 94 93 13 61 73 40 43 68 67 51 76 26 37 19 59 35 75 95 96 90 63 22 64 69 97 41 15 54 77 86 83
  4. setlocal enabledelayedexpansion
  5. for /f "delims=" %%a in (2.txt) do (
  6.         for %%b in (%%a) do (
  7.                 set /a #%%b+=1
  8.         )
  9. )
  10. for /f "delims=" %%a in (2.txt) do (
  11.         for %%b in (%%a) do (
  12.                 if !#%%b! equ 2 (
  13.                         if not defined ##%%b (
  14.                                 set str1=%%b !str1!
  15.                                 set str2=%%b !str2!
  16.                                 set /a m+=1
  17.                                 set ##%%b=1
  18.                         )
  19.                 ) else (
  20.                         set /a n+=1
  21.                         set _!random!_!n!=%%b
  22.                 )
  23.         )
  24. )
  25. for /f "tokens=2 delims==" %%a in ('set _') do (
  26.         if !m! lss 50 (
  27.                 set str1=%%a !str1!
  28.                 set /a m+=1
  29.         ) else (
  30.                 set str2=%%a !str2!
  31.         )
  32. )
  33. call :1 "!str1!"
  34. call :1 "!str2!"
  35. pause & exit
  36.         :1
  37.         setlocal
  38.         for %%a in (%~1) do (
  39.                 set /a k+=1
  40.                 set @!random!_!k!=%%a
  41.         )
  42.         for /f "tokens=2 delims==" %%a in ('set @') do (
  43.                 set newstr=%%a !newstr!
  44.         )
  45.         echo !newstr!
  46.         endlocal
复制代码

作者: Nsqs    时间: 2023-9-4 20:03

  1. $arr1=100..199 -replace '^1'
  2. 1..500|%{
  3.     $arr=$arr1|Get-Random -Count 100
  4.     $arr[0..49] -join ' '
  5.     $arr[50..99] -join ' '
  6. } > 结果.txt
复制代码
根据17楼思路,代码还能再短,反正这个数据量也不大,计算量也小
作者: qixiaobin0715    时间: 2023-9-5 12:53

本帖最后由 qixiaobin0715 于 2023-9-5 13:13 编辑

回复 23# pd1
以下代码根据你在23楼的思路写的,由于比较匆忙,看起来不是很明晰,未考虑效率问题,测试约1分钟左右。简要说明如下:
1.每次循环需要得到100个给定字符使用次数。(第17行)
2.判断给定的字符使用次数是否达到上限,若达到上限则忽略,否则按正常方法设置变量,以便继续使用。(第9行)
3.判断给定的字符尚可使用的次数是否达到剩余行数,若是,则本次优先使用(通过在未达到剩余行数上限的字符的变量名前加上字母,使达到剩余行数的字符变量在后面的循环中优先排在前面)。(第19、6-10行)
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. (for /l %%i in (1,1,1000) do (
  4.         for /l %%j in (0,1,99) do (
  5.         set num=0%%j
  6.         if !@%%j! equ 0 (
  7.             set _!random!_%%j=!num:~-2!
  8.         ) else (
  9.             if !#%%j! neq 500 set _a!random!_%%j=!num:~-2!
  10.         )
  11.     )
  12.     set n=0
  13.     for /f "tokens=1-3 delims==_" %%a in ('set _') do (
  14.         set /a n+=1
  15.         if !n! leq 50 (
  16.             set str=!str! %%c
  17.             set /a #%%b+=1
  18.         )
  19.         set /a @%%b=1000-%%i-500+#%%b
  20.         set _%%a_%%b=
  21.     )
  22.     echo,!str!
  23.     set str=
  24. ))>1.txt
  25. pause
复制代码

作者: buyiyang    时间: 2023-9-5 22:31

回复 36# qixiaobin0715


    这个算法也不错,不过会导致最后几十行连续含有相同的数字。
作者: qixiaobin0715    时间: 2023-9-6 12:06

回复 37# buyiyang
这是预料之中的事,后面生成的行属于查漏补缺,参见说明第2点。由于前面使用给定字符的频率陆续达到楼主所说的500次,这些字符在后面不能再继续使用,这些字符不再被设置为变量,请看代码第9行,所以可选的字符会越来越少,就会造成你所说的现象。
我觉得这种情况应当无关大局。如果非要消除这种情况的出现,可以通过调整部分 行的顺序来解决,方法很多,在这里举2个例子供参考:
1.掐头藏尾法或者叫移花接木法
思路是把前面若干行移到最后。
比如把前100行保存为2.txt,其余保存为1.txt,用copy或其它命令将2.txt合并到1.txt末尾。
替换第22行代码如下
  1. if %%i leq 100 (
  2.     echo,!str!>>2.txt
  3. ) else (
  4.     echo,!str!
  5. )
复制代码
然后将1.txt和2.txt在代码pause前完成合并。

实际上叫障眼法更准确。  
2.二次排列法
替换代码第22行
  1. echo,!random!:!str!
复制代码
在pause前增加如下代码
  1. sort 1.txt>2.txt
  2. (for /f "tokens=1*" %%i in (2.txt) do echo,%%j)>1.txt
  3. del 2.txt
复制代码

作者: aloha20200628    时间: 2023-9-6 13:01


这是一个数学中的排列问题》从n个元素中取出m个不同元素的不同排列数,如果m=n,则变为全排列问题。
本帖可变为两个全排列问题,例如00-99被分成00-49和50-99两组(亦可被分成奇数和偶数两组),各组完成500(次/行)全排列共合成1000行,正好满足lz的要求。
其后就是如何用bat/cmd/vbs/js/ps/...更显其能,实现每组(如00-49和50-99,或奇数组和偶数组)的500次/行全排列...

作者: qixiaobin0715    时间: 2023-9-8 10:52

本帖最后由 qixiaobin0715 于 2023-9-12 09:12 编辑

实际上可以将楼主顶楼的例子改成更通用的问题:
1.从00-99中,随机抽取50个不重复的字符,组成1行,每行50列。
2.抽取n次组成n行。(n为偶数)
3.最终的文本要控制每个字符个数均等,即00-99每个字符均要出现n/2次。
代码中n要求自定义,可以随意更改,只要是偶数即可。不使用两两互补法。
作者: 思想之翼    时间: 2023-9-8 12:42

本帖最后由 思想之翼 于 2023-9-9 17:02 编辑

回复 40# qixiaobin0715
n=2,两两互补
作者: qixiaobin0715    时间: 2023-9-8 12:48

本帖最后由 qixiaobin0715 于 2023-9-8 12:50 编辑

我是说代码不使用两两互补“法”。
不是说不能出现两两互补的情况。




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2