[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
个人感觉这个题目可能解法相对比较单一,并没有太多“百花齐放”的余地

TOP

特殊情况有特殊解法:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "skip=11 useback" %%a in ("%~0") do set str=!str!%%a
  3. for /l %%a in (40 -1 1) do (
  4. set /a ra=!random!%%%%a,m=~-%%a%%8
  5. for %%b in (!ra!) do (
  6. set /p=!str:~%%b,1!<nul
  7. for %%c in (!str:~%%b,1!) do set str=!str:%%c=!
  8. )
  9. if !m!==0 echo;
  10. )
  11. pause>nul&exit
  12. 我们去北大踢球吧
  13. 快上那儿等着信号
  14. 你不能离开大门口
  15. 生与死就在一瞬间
  16. 青春已从身边溜走
复制代码

TOP

本帖最后由 zm900612 于 2011-4-23 22:24 编辑
10# zm900612
下面是阁下代码的一次运行结果:
就死门离们上吧间
死生就着们踢离大
开我不离儿去去儿
快等北快北我去快
我大大们去们们我
batman 发表于 2011-4-21 18:59

当时只是为了表达思路,几分钟搞出来也没测试就回了,所以没发现这个问题...出错原因是漏了"",而且后来发现给定的文字中有重字,所以不能用变量替换取巧。
原帖就不改了,发一下新代码,这回是调试过无误的:
  1. @echo off&setlocal enabledelayedexpansion&echo %time%
  2. for /f "skip=14 useback" %%a in ("%~0") do set str=!str!%%a
  3. for /l %%a in (1 1 39) do set .!str:~%%a,1!=%%a
  4. for /l %%a in (40 -1 1) do (
  5. set /a "ra=!random!%%%%a","m=~-%%a%%8",n=ra+1
  6. for %%b in ("str:~!ra!,1") do (
  7. set /a "p=(40-%%a)-.!%%~b!","x=(40-%%a)%%8-.!%%~b!%%8","y=(40-%%a)/8-.!%%~b!/8"
  8. set "echo=!echo!!%%~b!"&set "xy=!xy!   !x!: !y!   "
  9. for /f "tokens=1* delims=@" %%c in ("!ra!@!n!") do set str=!str:~0,%%~c!!str:~%%~d!
  10. )
  11. )
  12. for /l %%a in (0 8 32) do echo !echo:~%%a,8!
  13. echo %xy: -=-%
  14. echo %time%&pause>nul&exit
  15. 我们去北大踢球吧
  16. 快上那儿等着信号
  17. 你不能离开大门口
  18. 生与死就在一瞬间
  19. 青春已从身边溜走
复制代码
优化了一下,用时减少一半
1

评分人数

    • batman: 好!思路绝了!PB + 20

TOP

其实我本意只是想提出一个随机取值的思路,因为常规思路要定义大量变量,很容易影响P的整体性能,不过话说回来,估计那个办法大概早有人发明了吧...

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

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

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

TOP

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

TOP

16# batman


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

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

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

31# terse


算法问题,用call的初衷是解决for参数中不能使用变量的弊端。
不过刚刚想起来,如果不存在重复字符,就可以直接用变量替换而无需用for截取了

TOP

33# terse
确实,忽略了


27# neorobin

测试环境中文编码未处理好, 以下用40个英文字母替代中文文本echo off
setlocal enableDelayedExpansion
set nl=^


for /l %%i in (0x100 1 0x127) do set nums=!nums!%%i,
for /l %%i in (40 -1 1) do (
  ...
neorobin 发表于 2011-4-23 00:45

那天测试的时候出错,后来发现是我不小心删了最后一个字符...
今天仔细研究了一下,虽然代码长,但是效率呱呱的,原因是通过将待随机取值的可用序号定义为一条长变量(这招太漂亮了),进而将输出的文字、偏移量各自定为一个长变量,创造出其他算法所无法做到的“干净环境”。而且在for /l 中以八进制、十六进制表示数字的用法我以前想都没想过,真是长见识了,这个新鲜技巧稍加磨砺,必定又是一柄利器。

TOP

返回列表