[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
14楼的思路是比较巧妙,但美中不足就是set/p输出影响了效率。。。

下面给出一种乱序的方法(先替换a为#,再替换b为a,最后替换#为b从而达到a、b互换的效果):
  1. @echo off&setlocal enabledelayedexpansion
  2. set "#0=&echo,"
  3. for /f "skip=22" %%a in (%~0) do set "str=!str!%%a"
  4. for /l %%a in (0,1,39) do (
  5.     set /a a=!random!%%40
  6.     for %%b in (!a!) do (
  7.         set "a=!str:~%%b,1!"
  8.         for %%b in ("!str:~%%a,1!") do (
  9.             for %%c in (!a!) do (
  10.                 set "str=!str:%%c=#!"
  11.                 set "str=!str:%%~b=%%c!"
  12.                 set "str=!str:#=%%~b!"
  13.             )
  14.         )
  15.     )
  16. )
  17. for /l %%a in (0,1,39) do (
  18.     set /a "a=(%%a+1)%%8"
  19.     for %%b in (!a!) do set "var=!var!!str:~%%a,1!!#%%b!"
  20. )
  21. echo %var%
  22. pause>nul&goto :eof
  23. 我们去北大踢球吧
  24. 快上那儿等着信号
  25. 你不能离开大门口
  26. 生与死就在一瞬间
  27. 青春已从身边溜走
复制代码
 注:因为每次都是两个汉字互换位置(有时可能是自己换自己),所以偏移量的计算会要借助临时文件来实现。
***共同提高***

TOP

另一种乱序输出思路:
  1. @echo off&setlocal enabledelayedexpansion&2>nul 3>nul echo %time%
  2. for /f "skip=14 useback" %%a in ("%~0") do set str=!str!%%a
  3. if "%1"==":" (
  4. for /l %%a in (0 1 39) do echo !random!@!str:~%%a,1!@%%a
  5. exit /b
  6. )
  7. for /f "tokens=2,3 delims=@" %%a in ('%0 : ^|sort') do (
  8. set echo=!echo!%%a
  9. set /a x=n%%8-%%b%%8,y=n/8-%%b/8,n+=1,m=n%%8,1/m||set echo=!echo!,
  10. set "xy=!xy!   !x!: !y!   "
  11. )
  12. for %%a in (!echo!) do echo %%a
  13. echo %xy: -=-%
  14. echo %time%&pause>nul&exit
  15. 我们去北大踢球吧
  16. 快上那儿等着信号
  17. 你不能离开大门口
  18. 生与死就在一瞬间
  19. 青春已从身边溜走
复制代码

TOP

楼上这个,方法跟我那个差不多吧,只是我用set 排序,你用sort。

最早的时候,我想过一种类似洗扑克牌的乱序方法。

1:随机将后一个字符放在前一个字符的前面或者后面,组合成新的字符串。
2:将新字符串再一次洗牌,循环随机20-40次。(估计了一下40个字符20次差不多能做到足够乱序了)

只是思路的一种吧,细想了一下,发现效率不怎么高,就没继续了。

TOP

本帖最后由 zm900612 于 2011-4-22 13:21 编辑

不一样吧,我的思路是直接分析自身输出,不需要定义大量变量
要不是字数已经超过31,用tokens+random来截取估计是最快的办法了

TOP

本帖最后由 caruko 于 2011-4-22 14:07 编辑

这个在随机排序大量文本的时候是不错,没有临时,没有大量变量,但是效率有点低。

1:echo更耗时,增加了echo次数。
2:sort 本身也是很耗内存的,sort /? 帮助命令看一下就能了解。N个排序对象,在SORT里,产生N个变量用来存放是至少的。set的机制不了解,但似乎在变量存放的时候就是有序的。
3:“ | sort + for”,echo完所有内容进入管道后,SORT才开始排序,SORT排序完并输出所有内容后,FOR开始读取。 所以耗时成倍增加。

TOP

这只是另一种思路而已,总有适合它的背景。上次生命游戏给我的教训是变量越多速度越慢,以至于后来一个循环耗时前后居然相差了十几倍,所以现在写代码时总会考虑变量的增加对效率的影响

TOP

的确,变量的增加让批处理速度变慢,但好像变慢的是变量读取速度,在频繁读取变量的时候差距就越大。

原因猜想:
可能真的是因为变量有序存放的原因?类似数据库?
不像一般的程序语言机制,只是增加内存消耗,没有明显增加读取耗时。

TOP

我写了这个测试代码
  1. @echo off&setlocal ENABLEDELAYEDEXPANSION
  2. for /l %%i in (1,1,10000) do set /a _%%i=%%i
  3. echo !time!
  4. for /l %%i in (1,1,10000) do set /a _1+=2
  5. echo !time!
  6. for /l %%i in (1,1,10000) do set /a _%%i+=2
  7. echo !time!
复制代码
同样是1万个变量
1:只读取一个变量进行计算,耗时6秒。
2:所有变量都读取进行计算,耗时16秒。

TOP

前面回复直接 40 个ECHO 其实和17楼900612兄一样思路
  1. @echo off&setlocal enabledelayedexpansion
  2. if "%1"=="" (
  3.    set N=0
  4.    for /f "skip=15 useback" %%i in ("%~0") do set str!n!=%%i&set/a n+=1
  5.    for /f "tokens=2-4" %%a in ('%0 $^|sort') do (
  6.        set /a "N=L/8,M=L%%8,X=%%b-N,Y=%%c-M,L+=1"
  7.        for %%j in (!N!) do (
  8.            set _%%j=!_%%j! !X!:!Y!
  9.            if !M! equ 7 (echo !#%%j!%%a) else set #%%j=!#%%j!%%a
  10.        )
  11.    )
  12.        for /l %%i in (0 1 4) do echo!_%%i!
  13.        pause&exit
  14. ) else for /l %%i in (0 1 4) do for /l %%j in (0 1 7) do echo !random!!random! !str%%i:~%%j,1! %%i %%j
  15.   exit
  16. 我们去北大踢球吧
  17. 快上那边等着信号
  18. 你不能离开大门口
  19. 生与死就在一瞬间
  20. 青春已从身边溜走
复制代码

TOP

16# batman


原先我也用了一点变量替换,但是总是莫名其妙少掉一个字,后来发现给定的文本中含有两个“大”,所以变量替换会出错

TOP

本帖最后由 terse 于 2011-4-22 17:51 编辑

变量太多影响效率  练手了
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f %%i in ('"find /c /v "" < %0"') do set/a n=%%i-5
  3. for /f "skip=%n% useback" %%i in ("%~0") do set str=!str!%%i
  4. for /l %%i in (0 1 39) do set i=10%%i&set var=!var!#!i:~-2!&set #!i:~-2!=%%i
  5. for /l %%i in (40 -1 1) do (
  6.     set /a "R=!random!%%%%i*3,N=(40-%%i)/8,M=(40-%%i)%%8"
  7.     for /f "tokens=1-3" %%a in ("!R! !N! !M!") do (
  8.         for %%j in ("!var:~%%a,3!") do for %%k in (!%%~j!) do (
  9.             set "var=!var:%%~j=!"
  10.             set /a "AN=%%k/8-%%b,AM=%%k%%8-%%c"
  11.             if %%c equ 7 (echo !#b!!str:~%%k,1!&set #b=) else set #b=!#b!!str:~%%k,1!
  12.             set _%%b=!_%%b! !AN!:!AM!
  13.         )
  14.     )
  15. )
  16. for /l %%i in (0 1 4) do echo!_%%i!
  17. pause&exit
  18. 我们去北大踢球吧
  19. 快上那边等着信号
  20. 你不能离开大门口
  21. 生与死就在一瞬间
  22. 青春已从身边溜走
复制代码

TOP

测试环境中文编码未处理好, 以下用40个英文字母替代中文文本:
  1. @echo off
  2. setlocal enableDelayedExpansion
  3. set nl=^
  4. for /l %%i in (0x100 1 0x127) do set nums=!nums!%%i,
  5. for /l %%i in (40 -1 1) do (
  6.   set /a rad=!random!%%%%i*4
  7.   for %%p in (!rad!) do (
  8.     set get=!nums:~%%p,3!
  9.     set /a val=get"&0xff
  10.     set ind=!ind!!val!,
  11.     for %%a in (!get!) do set nums=!nums:%%a,=!
  12.   )
  13. )
  14. for /f "usebackq delims=" %%l in ("%~0") do (
  15.   if /i "%%l"==":endText" set getText=0
  16.   if $!getText!==$1 set str=!str!%%l
  17.   if /i "%%l"==":text" set getText=1
  18. )
  19. for %%i in (!ind!) do (
  20.   set new=!new!!str:~%%i,1!
  21.   set /a lineOffs=cnt/8-%%i/8,colOffs=cnt%%8-%%i%%8
  22.   set "offs=!offs!!lineOffs!:!colOffs! "
  23.   if #!rem!==#7 set new=!new!!nl!& set offs=!offs!!nl!
  24.   set /a cnt+=1,rem=cnt%%8
  25. )
  26. echo !new!
  27. echo !offs!
  28. pause
  29. exit
  30. :text
  31. abcdefgh
  32. ijklmnop
  33. qrstuvwx
  34. yzABCDEF
  35. GHIJKLMN
  36. :endText
复制代码
1

评分人数

    • zm900612: 好!原来可以这么用...技术 + 1

TOP

本帖最后由 zm900612 于 2011-4-23 22:23 编辑

16# batman

纯粹用变量替换存在重字bug(有两个“大”),我改进了下,但是用太多call,效率恐怕很低,感叹一下,要是for的参数能带变量该多好啊...
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "useback skip=10" %%a in ("%~0") do set "str=!str!%%a"
  3. set str=@!str!
  4. for /l %%a in (0,1,39) do (
  5.     for %%b in (!random:~-1!) do call :str !str:~%%b,1!
  6. )
  7. for /l %%a in (1 8 33) do echo !str:~%%a,8!
  8. pause>nul
  9. :str
  10. for /f "tokens=1* delims=%1" %%a in ("!str!") do set "str=%%a!str:*%1=!%1"&exit /b
  11. 我们去北大踢球吧
  12. 快上那儿等着信号
  13. 你不能离开大门口
  14. 生与死就在一瞬间
  15. 青春已从身边溜走
复制代码
修改了下漏洞

TOP

本帖最后由 zm900612 于 2011-4-24 11:29 编辑

若给定文本中不存在重字,这样比原始的40次变量替换更快,而且random的范围更加合理:
  1. @echo off&setlocal enabledelayedexpansion&echo %time%
  2. for /f "useback skip=11" %%a in ("%~0") do set "str=!str!%%a"
  3. set str=@!str!
  4. for /l %%a in (0,1,16) do (
  5.     set /a a=!random!%%12+1
  6.     for %%b in (!a!) do call :str !str:~%%b,5!
  7. )
  8. for /l %%a in (1 8 33) do echo !str:~%%a,8!
  9. echo %time%&pause>nul
  10. :str
  11. for /f "tokens=1* delims=%1" %%a in ("!str!") do set "str=%%a!str:*%1=!%1"&exit /b
  12. 我们去北京踢球吧
  13. 快上那儿等着信号
  14. 你不能离开大门口
  15. 生与死就在一瞬间
  16. 青春已从身边溜走
复制代码
修改了“北大”为“北京”,16次call*5个字符的偏移范围,至少能把这40个字符循环两遍,保证随机性,同时random的取值范围降低,保证各部分字符串被随机选中的概率分布更为合理。
总之在效率和合理性上做了比较大的改进,相比原算法,最大的缺点就在于不支持重复字


现在发现,这似乎就是caruko说的“洗牌”算法,而且terse兄指出我绕弯了,其实只需要优化batman的代码就可以了...纯随机输出无重字文本时,这个大概是极限了(简化后的batman变量替换算法):
  1. @echo off&setlocal enabledelayedexpansion&echo %time%
  2. for /f "skip=9 useback" %%a in ("%~0") do set "str=!str!%%a"
  3. for /l %%a in (0,1,40) do (
  4. for %%b in (!random:~-1!) do (
  5. for /f %%c in ("!str:~%%b,1!") do set str=!str:%%c=!%%c
  6. )
  7. )
  8. for /l %%a in (0 8 32) do echo !str:~%%a,8!
  9. echo %time%&pause>nul
  10. 我们去北京踢球吧
  11. 快上那儿等着信号
  12. 你不能离开大门口
  13. 生与死就在一瞬间
  14. 青春已从身边溜走
复制代码
另有简化后的变量偏移的算法,效率和变量替换相差无几,支持重复字,但是代码多了两行:
  1. ;@echo off&setlocal enabledelayedexpansion&echo %time%
  2. ;for /f "useback" %%a in ("%~0") do set str=!str!%%a
  3. ;for /l %%a in (40 -1 1) do (
  4. ;        set /a "ra=!random!%%%%a",n=ra+1
  5. ;        for /f "tokens=1* delims=@" %%b in ("!ra!@!n!") do (
  6. ; set echo=!echo!!str:~%%b,1!
  7. ; set "str=!str:~0,%%b!!str:~%%c!"
  8. ; )
  9. ;)
  10. ;for /l %%a in (0 8 32) do echo !echo:~%%a,8!
  11. ;echo %time%&pause>nul&exit
  12. 我们去北大踢球吧
  13. 快上那儿等着信号
  14. 你不能离开大门口
  15. 生与死就在一瞬间
  16. 青春已从身边溜走走
复制代码
caruko提出的洗牌法,优点是可以轻易控制复杂程度和用时,缺点是经常洗不干净:
  1. ;@echo off&setlocal enabledelayedexpansion&echo %time%
  2. ;for /f "useback" %%a in ("%~0") do set str=!str!%%a
  3. ;for /l %%a in (1 1 40) do (
  4. ; set /a ra=!random:~-1!+5
  5. ; for %%b in ("!ra!") do (
  6. ; set str=!str:~-%%~b!!str:~5,-%%~b!!str:~0,5!
  7. ; )
  8. ;)
  9. ;for /l %%a in (0 8 32) do echo !str:~%%a,8!
  10. ;echo %time%&pause>nul&exit
  11. 我们去北大踢球吧
  12. 快上那儿等着信号
  13. 你不能离开大门口
  14. 生与死就在一瞬间
  15. 青春已从身边溜走
复制代码
以上几种算法都存在不易计算偏移量的先天缺陷,单独乱序输出时表现不错,但是若要同时计算偏移量,耗时将暴涨,此贴中另外三种在这方面则有优势:
常规算法就不提了,效率一般,也没什么艺术性。
读取自身输出作为输入的方案感觉已经无法再改进了,请见24楼terse代码。
最牛的应该是27楼neorobin的算法了,干净利落,浑然天成。

TOP

偏移量我就不计算了,那个与上述代码八字不合,写进来比较影响效率和美观

TOP

返回列表