[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
手算开根方案的初步实现,有bug待修,支持大数开根,效率没有很高,先刷新16楼那段代码的时间。
精度80位大约耗时8秒。应该可以再改,再看看吧。
  1. :: Bignum(integer) Square Root, Decimal Solution
  2. :: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Decimal_(base_10)
  3. :: 523066680/vicyang
  4. :: 2019-01
  5. @echo off
  6. setlocal enabledelayedexpansion
  7. :init
  8.     rem 创建用于计算字符串长度的模板,长度限制为 2^pow
  9.     set "sharp=#"
  10.     set /a pow=11, maxlen=1^<^<pow
  11.     for /l %%a in (1,1,%pow%) do set sharp=!sharp!!sharp!
  12. set num=2
  13. rem set num=10
  14. rem call :get_int_of_root %num% int_root cmp
  15. set precision=80
  16. rem call :check_first %num% %precision%
  17. call :decimal_solution %num%
  18. pause
  19. exit /b
  20. :check_first
  21.     perl -Mbignum=p,-%2 -le "print sqrt(%1)" 2>nul
  22.     goto :eof
  23. :: 手算开根方案
  24. :decimal_solution
  25.     setlocal
  26.     set num=%1
  27.     set tnum=%1
  28.     call :length %num% len
  29.     set /a mod=len %% 2, tlen=len, base=0
  30.     if %mod% equ 1 (set /a skip=1) else (set /a skip=2)
  31.     set target=!tnum:~0,%skip%!
  32.     set tnum=!tnum:~%skip%!
  33.     set mp_0=0
  34.     rem prec 精度
  35.     set /a prec = 0
  36.     set /a tbase_len = 0, equ = 0
  37.     :dec_loop
  38.         set /a min=0, max=10, mid=5, range=max-min, quit=0, equ=0
  39.         set /a tbase_len+=1
  40.         call :length %target% target_len
  41.         :: 预估下一个可能的数,并限制二分搜索的最大值
  42.         :guess
  43.         if %target_len% gtr 3 (
  44.         if %target_len% equ %tbase_len% (
  45.             set /a t_head = %target:~0,2%, b_head = %base:~0,2%
  46.         ) else (
  47.             set /a t_head = %target:~0,3%, b_head = %base:~0,2%
  48.         )
  49.         ) else (goto :out_of_guess)
  50.         for /l %%a in (0,1,9) do (
  51.             set /a t = %%a * b_head
  52.             rem echo !t! !target:~0,2! %%a
  53.             if !t! gtr %t_head% (
  54.                 set /a max = %%a, mid = ^(min+max^)/2
  55.                 goto :out_of_guess
  56.             )
  57.         )
  58.         :out_of_guess
  59.         rem echo, &echo %base%%mid% %target% %tbase_len% %target_len% max: %max%
  60.         :dec_bin_search
  61.             :: mp = [base*10+mid] * mid
  62.             if "%base%" == "0" (
  63.                 set /a tbase = mid
  64.             ) else (
  65.                 set tbase=!base!!mid!
  66.             )
  67.             set ta=%time%
  68.             call :bignum_mp %tbase% %mid% %tbase_len% 1 mp mp_len
  69.             set mp_%mid%=%mp%
  70.             set mplen_%mid%=%mp_len%
  71.             rem call :cmp %mp% %target% %mp_len% %target_len% cmp
  72.             :: 比较 - 判断是否超出
  73.             :cmp_begin
  74.             if %mp_len% gtr %target_len% (set /a cmp=1&goto :cmp_end)
  75.             if %mp_len% lss %target_len% (set /a cmp=-1&goto :cmp_end)
  76.             :: 如果长度相同,直接按字符串对比
  77.             if "%mp%" gtr "%target%" (set /a cmp=1&goto :cmp_end)
  78.             if "%mp%" lss "%target%" (set /a cmp=-1&goto :cmp_end)
  79.             if "%mp%" equ "%target%" (set /a cmp=0&goto :cmp_end)
  80.             :cmp_end
  81.             rem call :time_delta %ta% %time% bs_tu
  82.             if %cmp% equ 0 (set /a quit=1, equ=1)
  83.             if %cmp% equ 1 (set /a max=mid )
  84.             if %cmp% equ -1 (set /a min=mid )
  85.             if %range% leq 1 ( set /a quit=1 )
  86.             set /a mid=(max+min)/2, range=max-mid
  87.         if %quit% == 0 goto :dec_bin_search
  88.         
  89.         set ta=%time%
  90.         set /p inp="%mid%"<nul
  91.         rem echo, &echo tnum %tnum%, cmp %cmp%, equ %equ%, tg %target%
  92.         if "%tnum%" == "" (
  93.             if %cmp% == 0 (
  94.                 goto :dec_loop_out
  95.             ) else (
  96.                 rem current precision
  97.                 if %prec% equ 0 set /p inp="."<nul
  98.                 set /a prec+=1
  99.             )
  100.         )
  101.         rem echo b=%base% tb=%tbase% tg=%target% mp=%mp% mid=%mid%
  102.         call :bignum_minus %target% !mp_%mid%! target
  103.         if %skip% geq %len% (
  104.             set target=%target%00
  105.         ) else (
  106.             if "%target%" == "0" (
  107.                 set target=!tnum:~0,2!
  108.             ) else (
  109.                 set target=!target!!tnum:~0,2!
  110.             )
  111.             set tnum=!tnum:~2!
  112.             set /a skip+=2
  113.         )
  114.         rem base=base*10+mid*2
  115.         if "%base%" == "0" (
  116.             set /a base=mid*2
  117.         ) else (
  118.             set /a db_mid=mid*2
  119.             call :bignum_plus !base!0 !db_mid! base
  120.         )
  121.         rem call :time_delta %ta% %time% else_tu
  122.     if %prec% leq %precision% (goto :dec_loop)
  123.     :dec_loop_out
  124.     endlocal
  125.     goto :eof
  126. ::大数乘法
  127. :bignum_mp
  128.     setlocal
  129.     set num_a=%1
  130.     set num_b=%2
  131.     set /a len_a=%3, len_b=%4
  132.     for /l %%b in ( 1, 1, %len_b% ) do ( set ele_b=!ele_b! !num_b:~-%%b,1! )
  133.     for /l %%a in ( 1, 1, %len_a% ) do ( set ele_a=!ele_a! !num_a:~-%%a,1! )
  134.     set /a id = 0, sid = 0, maxid = 0
  135.     for %%b in ( %ele_b% ) do (
  136.         set /a sid = id, id += 1
  137.         for %%a in ( %ele_a% ) do (
  138.             set /a buff[!sid!] += %%a * %%b, sid += 1, maxid = sid
  139.         )
  140.     )
  141.     rem Merge
  142.     set /a id = 0
  143.     for /l %%c in ( 0, 1, %maxid% ) do (
  144.         set /a next = %%c+1
  145.         set /a buff[!next!] += buff[%%c]/10, buff[%%c] = buff[%%c] %% 10
  146.     )
  147.     if "!buff[%maxid%]!" == "0" set /a maxid-=1
  148.     set product=
  149.     for /l %%n in (%maxid%, -1, 0) do set product=!product!!buff[%%n]!
  150.     endlocal &set %5=%product%&set /a %6=%maxid%+1
  151.     goto :eof
  152. ::大数加法
  153. :bignum_plus
  154.     setlocal
  155.     set num_a=%1
  156.     set num_b=%2
  157.     call :length %num_a% len_a
  158.     call :length %num_b% len_b
  159.     set /a max = len_a
  160.     if %len_b% gtr %len_a% (set /a max=len_b, len_b=len_a&set num_a=%num_b%&set num_b=%num_a%)
  161.     for /l %%n in ( 1, 1, %max% ) do (
  162.         if %%n leq %len_b% (
  163.             set /a buff[%%n] = !num_a:~-%%n,1! + !num_b:~-%%n,1!
  164.         ) else (
  165.             set buff[%%n]=!num_a:~-%%n,1!
  166.         )
  167.     )
  168.     set /a id = 0
  169.     for /l %%c in ( 0, 1, %max% ) do (
  170.         set /a next = %%c+1
  171.         set /a buff[!next!] += buff[%%c]/10, buff[%%c] = buff[%%c] %% 10
  172.     )
  173.     if "!buff[%next%]!" gtr "0" set /a max+=1
  174.     set sum=
  175.     for /l %%a in (%max%, -1, 1) do set sum=!sum!!buff[%%a]!
  176.     endlocal &set %3=%sum%
  177.     goto :eof
  178. ::大数减法
  179. :bignum_minus
  180.     setlocal
  181.     set num_a=%1
  182.     set num_b=%2
  183.     call :length %num_a% len_a
  184.     call :length %num_b% len_b
  185.     set /a max = len_a
  186.     if %len_b% gtr %len_a% (set /a max=len_b, len_b=len_a&set num_a=%num_b%&set num_b=%num_a%)
  187.     set /a minus = 0
  188.     for /l %%n in ( 1, 1, %max% ) do (
  189.         if %%n leq %len_b% (
  190.             set /a dt = !num_a:~-%%n,1! - !num_b:~-%%n,1! - minus
  191.         ) else (
  192.             set /a dt = !num_a:~-%%n,1! - minus
  193.         )
  194.         if !dt! lss 0 (
  195.             set /a buff[%%n] = dt + 10, minus=1
  196.         ) else (
  197.             set /a buff[%%n] = dt, minus=0
  198.         )
  199.     )
  200.     set delta=#
  201.     for /l %%a in (%max%, -1, 1) do set delta=!delta:#0=#!!buff[%%a]!
  202.     endlocal &set %3=%delta:#=%
  203.     goto :eof
  204. ::字符串长度计算
  205. :length %str% %vname%
  206.     setlocal
  207.     set test=%~1_%sharp%
  208.     set test=!test:~0,%maxlen%!
  209.     set test=%test:*_=%
  210.     set /a len=maxlen-(%test:#=1+%1)
  211.     endlocal &set %2=%len%
  212.     goto :eof
  213. :: plp626的时间差函数 时间跨度在1分钟内可调用之;用于测试一般bat运行时间
  214. :time_delta <beginTimeVar> <endTimeVar> <retVar> // code by plp626
  215.     setlocal
  216.     set ta=%1&set tb=%2
  217.     set /a "c=1!tb:~-5,2!!tb:~-2!-1!ta:~-5,2!!ta:~-2!,c+=-6000*(c>>31)"
  218.     if defined %3 set /a c+=!%3!
  219.     endlocal&set %3=%c%
  220.     goto:eof
复制代码

TOP

本帖最后由 523066680 于 2019-1-5 21:02 编辑

回复 35# SQYSQYSQY

    当我看了 happy886r 的乘法,已经知道划分为N位一段而非逐位的处理可以令速度翻倍。参考23楼 http://bbs.bathome.net/redirect. ... 1557&pid=216162
我当然也知道 for 比 goto 快, goto 比 call 快,但我是不会让我的代码变成这样的。

不过这是批处理,如果我不花时间继续优化,我就可以做别的有趣的事情了。
1

评分人数

    • 老刘1号: bat纠结什么效率,根本没有效率技术 + 1

TOP

本帖最后由 523066680 于 2019-1-5 23:07 编辑

回复 24# 小程936

    你是说Pi吗,很多大牛为此做过大的量工作。
传奇人物 Fabrice Ballard
有史以来最优秀的程序员有哪些? - absfree的回答 - 知乎

TOP

本帖最后由 523066680 于 2019-1-29 18:46 编辑

nothing

TOP

本帖最后由 523066680 于 2019-1-7 10:11 编辑

优化一波,没你的快。100位5.6秒,300位35秒 (CPU比较好,换一台机可能是8秒和45秒)
仍然使用逐位的计算,未使用连续N位数字为一段的处理方案,省心。

解决的问题:之前的代码对100开根会出现 10.0000000,现在不会了。
优化的部分:去掉了很多临时数组的操作特别是 buff[] 和与之对应的for循环
  1. :: Bignum(integer) Square Root, Decimal Solution
  2. :: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Decimal_(base_10)
  3. :: 523066680/vicyang
  4. :: 2019-01
  5. @echo off
  6. setlocal enabledelayedexpansion
  7. :init
  8.     rem 创建用于计算字符串长度的模板,长度限制为 2^pow
  9.     set "sharp=#"
  10.     set /a pow=11, maxlen=1^<^<pow
  11.     for /l %%a in (1,1,%pow%) do set sharp=!sharp!!sharp!
  12. set precision=80
  13. set num=2
  14. call :check_one %num%
  15. pause
  16. exit /b
  17. :: 独立测试
  18. :check_one
  19.     set ta=!time!
  20.     rem call :check_first %1 !precision!
  21.     call :decimal_solution %1
  22.     call :time_delta !ta! !time! tu
  23.     echo time used: !tu!
  24.     goto :eof
  25. :: 批量测试
  26. :check_all
  27.     for /l %%a in (1,1,99) do (
  28.         echo test number: %%a
  29.         call :check_first %%a !precision!
  30.         call :decimal_solution %%a
  31.         echo,
  32.     )
  33.     goto :eof
  34. :: 使用其他工具校验/对比结果
  35. :check_first
  36.     perl -Mbignum=p,-%2 -le "print sqrt(%1)" 2>nul
  37.     goto :eof
  38. :: 手算开根方案
  39. :decimal_solution
  40.     setlocal
  41.     set num=%1
  42.     set tnum=%1
  43.     call :length %num% len
  44.     set /a mod=len %% 2, tlen=len, base=0
  45.     if %mod% equ 1 (set /a skip=1) else (set /a skip=2)
  46.     set target=!tnum:~0,%skip%!
  47.     set tnum=!tnum:~%skip%!
  48.     set /a mp_0=0, mplen_0=1
  49.     set /a bstimes=0
  50.     rem prec 当前精度
  51.     set /a prec = 0
  52.     set /a base_len=0, equ=0, target_len=skip
  53.     :dec_loop
  54.         set /a min=0, max=10, mid=5, range=max-min, quit=0, equ=0
  55.         set /a tbase_len=base_len+1
  56.         :: 评估二分搜索的最大值
  57.         :guess
  58.         if %target_len% gtr 3 (
  59.         if %target_len% equ %tbase_len% (
  60.             set /a t_head = %target:~0,2%, b_head = %base:~0,2%
  61.         ) else (
  62.             set /a t_head = %target:~0,3%, b_head = %base:~0,2%
  63.         )
  64.         ) else (goto :out_of_guess)
  65.         for /l %%a in (0,1,9) do (
  66.             set /a t = %%a * b_head
  67.             if !t! gtr %t_head% (
  68.                 set /a max = %%a
  69.                 goto :out_of_guess
  70.             )
  71.         )
  72.         :out_of_guess
  73.         :: 做大致的除法预估 mid 值
  74.         :estimate
  75.         if %target_len% gtr 5 (
  76.             if %target_len% geq %tbase_len% (
  77.                 set /a est=!target:~0,6!/!base:~0,5!
  78.                 rem echo est - !est!
  79.                 set /a mid=!est:~0,1!
  80.                 rem echo,&echo %base% !target! !est! !mid! !target:~0,5!/!base:~0,5!
  81.             )
  82.         )
  83.         :: 如果预估max等于1,说明结果只能为0,跳过 bin_search
  84.         if %max% equ 1 (set /a mid=0& goto :out_bin_search )
  85.         rem echo, &echo %base%%mid% %target% %tbase_len% %target_len% max: %max%
  86.         set ta=%time%
  87.         :dec_bin_search
  88.             set /a bstimes+=1
  89.             :: mp = [base*10+mid] * mid
  90.             if "%base%" == "0" (
  91.                 set /a tbase = mid
  92.             ) else (
  93.                 set tbase=!base!!mid!
  94.             )
  95.             call :bignum_mp_single %tbase% %mid% %tbase_len% 1 mp mp_len
  96.             set mp_%mid%=%mp%
  97.             set mplen_%mid%=%mp_len%
  98.             :: 比较 - 判断是否超出
  99.             :cmp_begin
  100.             if %mp_len% gtr %target_len% (set /a cmp=1&goto :cmp_end)
  101.             if %mp_len% lss %target_len% (set /a cmp=-1&goto :cmp_end)
  102.             :: 如果长度相同,直接按字符串对比
  103.             if "%mp%" gtr "%target%" (set /a cmp=1&goto :cmp_end)
  104.             if "%mp%" lss "%target%" (set /a cmp=-1&goto :cmp_end)
  105.             if "%mp%" equ "%target%" (set /a cmp=0&goto :cmp_end)
  106.             :cmp_end
  107.             rem call :time_delta %ta% %time% bs_tu
  108.             if %cmp% equ 0 (set /a quit=1, equ=1)
  109.             if %cmp% equ 1 (set /a max=mid)
  110.             if %cmp% equ -1 (set /a min=mid)
  111.             if %range% leq 1 ( set /a quit=1 )
  112.             set /a mid=(max+min)/2, range=max-mid
  113.         if %quit% == 0 goto :dec_bin_search
  114.         :out_bin_search
  115.         rem echo, &echo est: %est%, act mid: %mid%
  116.         set /p inp="%mid%"<nul
  117.         if "%tnum%" == "" (
  118.             :: 如果target只剩下 00,方案结束
  119.             if "%target%" == "00" ( goto :dec_loop_out )
  120.             if %cmp% == 0 (
  121.                 goto :dec_loop_out
  122.             ) else (
  123.                 :: 当前精度
  124.                 if %prec% equ 0 set /p inp="."<nul
  125.                 set /a prec+=1
  126.             )
  127.         )
  128.         rem echo b=%base% tb=%tbase% tg=%target% mp=%mp% mid=%mid%
  129.         set ta=%time%
  130.         call :bignum_minus %target% !mp_%mid%! %target_len% !mplen_%mid%! target target_len
  131.         if %skip% geq %len% (
  132.             set target=%target%00
  133.         ) else (
  134.             if "%target%" == "0" (
  135.                 set target=!tnum:~0,2!
  136.             ) else (
  137.                 set target=!target!!tnum:~0,2!
  138.             )
  139.             set tnum=!tnum:~2!
  140.             set /a skip+=2
  141.         )
  142.         set /a target_len+=2
  143.         rem base=base*10+mid*2
  144.         if "%base%" == "0" (
  145.             set /a base=mid*2
  146.             if !base! geq 10 (set /a base_len=2) else (set /a base_len=1)
  147.         ) else (
  148.             set /a db_mid=mid*2
  149.             if !db_mid! geq 10 (set /a dbmidlen=2) else (set /a dbmidlen=1)
  150.             call :bignum_plus !base!0 !db_mid! !base_len!+1 !dbmidlen! base base_len
  151.         )
  152.     if %prec% leq %precision% (goto :dec_loop)
  153.     :dec_loop_out
  154.     echo,
  155.     echo search times: %bstimes%
  156.     endlocal
  157.     goto :eof
  158. :: 大数 乘以 单位数
  159. :bignum_mp_single
  160.     setlocal
  161.     set num_a=%1
  162.     set num_b=%2
  163.     set /a pool = 0, maxid = %3
  164.     set "res="
  165.     for /l %%a in ( 1, 1, %maxid% ) do (
  166.         set /a mp = !num_a:~-%%a,1! * num_b + pool, t = mp %% 10, pool = mp / 10
  167.         set res=!t!!res!
  168.     )
  169.     if %pool% neq 0 (
  170.         set /a maxid+=1
  171.         set res=!pool!!res!
  172.     )
  173.     endlocal&set %5=%res%&set %6=%maxid%
  174.     goto :eof
  175. ::大数加法
  176. :bignum_plus
  177.     setlocal
  178.     set num_a=%1
  179.     set num_b=%2
  180.     set /a len_a=%3, len_b=%4
  181.     set /a max = len_a
  182.     if %len_b% gtr %len_a% (set /a max=len_b, len_b=len_a&set num_a=%num_b%&set num_b=%num_a%)
  183.     set /a pool=0
  184.     set res=
  185.     for /l %%n in ( 1, 1, %max% ) do (
  186.         if %%n leq %len_b% (
  187.             set /a t = !num_a:~-%%n,1! + !num_b:~-%%n,1! + pool
  188.         ) else (
  189.             set /a t = !num_a:~-%%n,1! + pool
  190.         )
  191.         set /a mod = t %% 10, pool = t / 10
  192.         set res=!mod!!res!
  193.     )
  194.     if %pool% gtr 0 (set /a max+=1 &set res=1%res%)
  195.     endlocal &set %5=%res%&set %6=%max%
  196.     goto :eof
  197. ::大数减法
  198. :bignum_minus
  199.     setlocal
  200.     set num_a=%1
  201.     set num_b=%2
  202.     set /a len_a=%3, len_b=%4
  203.     set /a max = len_a
  204.     if %len_b% gtr %len_a% (set /a max=len_b, len_b=len_a&set num_a=%num_b%&set num_b=%num_a%)
  205.     set /a minus = 0
  206.     set "res="
  207.     for /l %%n in ( 1, 1, %max% ) do (
  208.         if %%n leq %len_b% (
  209.             set /a dt = !num_a:~-%%n,1! - !num_b:~-%%n,1! - minus
  210.         ) else (
  211.             set /a dt = !num_a:~-%%n,1! - minus
  212.         )
  213.         if !dt! lss 0 (
  214.             set /a t = dt + 10, minus=1
  215.         ) else (
  216.             set /a t = dt, minus=0
  217.         )
  218.         set res=!t!!res!
  219.         if !t! equ 0 (set /a zero+=1) else (set /a zero=0)
  220.     )
  221.     set res=!res:~%zero%!
  222.     endlocal &set %5=%res%&set /a %6=%max%-%zero%
  223.     goto :eof
  224. ::字符串长度计算
  225. :length %str% %vname%
  226.     setlocal
  227.     set test=%~1_%sharp%
  228.     set test=!test:~0,%maxlen%!
  229.     set test=%test:*_=%
  230.     set /a len=maxlen-(%test:#=1+%1)
  231.     endlocal &set %2=%len%
  232.     goto :eof
  233. :: plp626的时间差函数 时间跨度在1分钟内可调用之;用于测试一般bat运行时间
  234. :time_delta <beginTimeVar> <endTimeVar> <retVar> // code by plp626
  235.     setlocal
  236.     set ta=%1&set tb=%2
  237.     set /a "c=1!tb:~-5,2!!tb:~-2!-1!ta:~-5,2!!ta:~-2!,c+=-6000*(c>>31)"
  238.     if defined %3 set /a c+=!%3!
  239.     endlocal&set %3=%c%
  240.     goto:eof
复制代码
1

评分人数

TOP

回复 30# SQYSQYSQY

优化效率一时爽,添加功能火葬场。(就目前来说浮点数开根还没搞进去,也没有经过大量的数值测试BUG)
观点:功能完善前写给人看,方便调整,完善后再优化给机器看。

发现自己代码优化后有问题,29 26 开根会出现5.99999,29楼已经纠正,顺便提速,estimate 模块用于提前估值减少二分搜索次数。
在我的主机 80位 3.5s 300位 32s,手算法似乎不怎么需要二分,可以快速判定下一位数,绕了很大的弯路,修改中。

TOP

本帖最后由 523066680 于 2019-1-9 09:40 编辑

改好了,去掉了二分搜索,5位以内的情况下粗暴遍历。
通过0到100的数字开根测试,支持大数字。在这个基础上再去优化会舒服很多,但还是要做出浮点数开根以后再说。

CPU: i7 4GHz
SQRT 2 精度 80 耗时约 2.3s。精度 300,耗时约 20s
  1. :: Bignum(integer) Square Root, Decimal Solution
  2. :: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Decimal_(base_10)
  3. :: 523066680/vicyang
  4. :: 2019-01
  5. @echo off
  6. setlocal enabledelayedexpansion
  7. :init
  8.     rem 创建用于计算字符串长度的模板,长度限制为 2^pow
  9.     set "sharp=#"
  10.     set "serial=9876543210"
  11.     set /a pow=11, maxlen=1^<^<pow
  12.     for /l %%a in (1,1,%pow%) do set sharp=!sharp!!sharp!
  13. set precision=80
  14. rem call :check_one 2
  15. call :check_all
  16. pause
  17. exit /b
  18. :: 独立测试
  19. :check_one
  20.     set ta=!time!
  21.     rem call :check_first %1 !precision!
  22.     call :decimal_solution %1
  23.     call :time_delta !ta! !time! tu
  24.     echo time used: !tu!
  25.     goto :eof
  26. :: 批量测试
  27. :check_all
  28.     for /l %%a in (1,1,99) do (
  29.         echo test number: %%a
  30.         rem call :check_first %%a !precision!
  31.         call :decimal_solution %%a
  32.         echo,
  33.     )
  34.     goto :eof
  35. :: 使用其他工具校验/对比结果
  36. :check_first
  37.     perl -Mbignum=p,-%2 -le "print sqrt(%1)" 2>nul
  38.     goto :eof
  39. :: 手算开根方案
  40. :decimal_solution
  41.     setlocal
  42.     set num=%1
  43.     set tnum=%1
  44.     :: 计算长度,判断需要截取的目标长度(1 or 2)
  45.     call :length %num% len
  46.     set /a mod=len %% 2, skip = 2 - mod
  47.     set target=!tnum:~0,%skip%!
  48.     set tnum=!tnum:~%skip%!
  49.     set "base="
  50.     :: prec 当前精度
  51.     set /a prec = 0, base_len=0, target_len=skip
  52.     :dec_loop
  53.         :: 推算下一个数
  54.         :estimate
  55.             :: 如果目标值 小于 基数,下一个数字判定为0
  56.             call :cmp %target% %base%0 %target_len% %base_len%+1 cmp
  57.             if !cmp! equ -1 (
  58.                 set /a mid=0, mp=0, mplen=0
  59.                 goto :out_estimate
  60.             )
  61.             if %base_len% gtr 5 (
  62.                 set /a est=!target:~0,6!/!base:~0,5!
  63.             ) else (
  64.                 :: 在set/a计算范围内的,[粗暴]遍历
  65.                 for /l %%a in (0,1,10) do (
  66.                     set /a mp=%base%%%a*%%a
  67.                     if !mp! gtr !target! (set /a est=%%a-1 &goto :out_est_for)
  68.                 )
  69.             )
  70.             :out_est_for
  71.             :: 199999996400/1999999988 = 99.9999988
  72.             :: but 199999/19999 = 10
  73.             if %est% geq 10 (
  74.                 set /a tbase_len=base_len+1
  75.                 if !target_len! gtr !tbase_len! (set /a est=9)
  76.             )
  77.             set /a mid=!est:~0,1!
  78.             call :bignum_mp_single !base!!mid! !mid! !base_len!+1 1 mp mplen
  79.             call :cmp !mp! !target! !mplen! !target_len! cmp
  80.             :: 如果mp超出目标范围
  81.             if !cmp! equ 1 (
  82.                 set /a mid-=1
  83.                 call :bignum_mp_single !base!!mid! !mid! !base_len!+1 1 mp mplen
  84.             )
  85.         :out_estimate
  86.         set /p inp="%mid%"<nul
  87.         rem echo,&echo tg !target!, mp !mp!, base !base!, mid !mid!, est !est!
  88.         if "%tnum%" == "" (
  89.             :: 如果target只剩下 00,方案结束
  90.             if "%target%" == "00" ( goto :dec_loop_out )
  91.             if %cmp% == 0 ( goto :dec_loop_out )
  92.         )
  93.         :: 计算下一段target的值
  94.         call :bignum_minus %target% %mp% %target_len% %mplen% target target_len
  95.         :: 扩充target,如果被开根数已经截取完,直接补0,精度+1
  96.         if %skip% geq %len% (
  97.             set target=%target%00
  98.             set /a prec+=1
  99.             if !prec! equ 1 set /p inp="."<nul
  100.         ) else (
  101.             if "%target%" == "0" (set target=!tnum:~0,2!
  102.                           ) else (set target=!target!!tnum:~0,2!)
  103.             set tnum=!tnum:~2!
  104.             set /a skip+=2
  105.         )
  106.         set /a target_len+=2
  107.         :: 更新基数 - base
  108.         rem base=base*10+mid*2
  109.         if "%base%" == "0" (
  110.             set /a base=mid*2, base_len=1+base/10
  111.         ) else (
  112.             set /a db_mid=mid*2, dbmidlen=1+db_mid/10
  113.             call :bignum_plus !base!0 !db_mid! !base_len!+1 !dbmidlen! base base_len
  114.         )
  115.     if %prec% leq %precision% (goto :dec_loop)
  116.     :dec_loop_out
  117.     echo,
  118.     endlocal
  119.     goto :eof
  120. :: 比较
  121. :cmp
  122.     set /a La=%3, Lb=%4
  123.     if %La% gtr %Lb% (set /a %5=1&goto :eof)
  124.     if %La% lss %Lb% (set /a %5=-1&goto :eof)
  125.     :: 如果长度相同,直接按字符串对比
  126.     if "%1" gtr "%2" (set /a %5=1&goto :eof)
  127.     if "%1" lss "%2" (set /a %5=-1&goto :eof)
  128.     if "%1" equ "%2" (set /a %5=0&goto :eof)
  129. :: 大数 乘以 单位数
  130. :bignum_mp_single
  131.     setlocal
  132.     set num_a=%1
  133.     set num_b=%2
  134.     set /a pool = 0, maxid = %3
  135.     set "res="
  136.     for /l %%a in ( 1, 1, %maxid% ) do (
  137.         set /a mp = !num_a:~-%%a,1! * num_b + pool, t = mp %% 10, pool = mp / 10
  138.         set res=!t!!res!
  139.     )
  140.     if %pool% neq 0 (
  141.         set /a maxid+=1
  142.         set res=!pool!!res!
  143.     )
  144.     endlocal&set %5=%res%&set %6=%maxid%
  145.     goto :eof
  146. ::大数加法
  147. :bignum_plus
  148.     setlocal
  149.     set num_a=%1
  150.     set num_b=%2
  151.     set /a len_a=%3, len_b=%4
  152.     set /a max = len_a
  153.     if %len_b% gtr %len_a% (set /a max=len_b, len_b=len_a&set num_a=%num_b%&set num_b=%num_a%)
  154.     set /a pool=0
  155.     set res=
  156.     for /l %%n in ( 1, 1, %max% ) do (
  157.         if %%n leq %len_b% (
  158.             set /a t = !num_a:~-%%n,1! + !num_b:~-%%n,1! + pool
  159.         ) else (
  160.             set /a t = !num_a:~-%%n,1! + pool
  161.         )
  162.         set /a mod = t %% 10, pool = t / 10
  163.         set res=!mod!!res!
  164.     )
  165.     if %pool% gtr 0 (set /a max+=1 &set res=1%res%)
  166.     endlocal &set %5=%res%&set %6=%max%
  167.     goto :eof
  168. ::大数减法
  169. :bignum_minus
  170.     setlocal
  171.     set num_a=%1
  172.     set num_b=%2
  173.     set /a len_a=%3, len_b=%4
  174.     set /a max = len_a
  175.     if %len_b% gtr %len_a% (set /a max=len_b, len_b=len_a&set num_a=%num_b%&set num_b=%num_a%)
  176.     set /a minus = 0
  177.     set "res="
  178.     for /l %%n in ( 1, 1, %max% ) do (
  179.         if %%n leq %len_b% (
  180.             set /a dt = !num_a:~-%%n,1! - !num_b:~-%%n,1! - minus
  181.         ) else (
  182.             set /a dt = !num_a:~-%%n,1! - minus
  183.         )
  184.         if !dt! lss 0 (
  185.             set /a t = dt + 10, minus=1
  186.         ) else (
  187.             set /a t = dt, minus=0
  188.         )
  189.         set res=!t!!res!
  190.         if !t! equ 0 (set /a zero+=1) else (set /a zero=0)
  191.     )
  192.     set res=!res:~%zero%!
  193.     endlocal &set %5=%res%&set /a %6=%max%-%zero%
  194.     goto :eof
  195. ::字符串长度计算
  196. :length %str% %vname%
  197.     setlocal
  198.     set test=%~1_%sharp%
  199.     set test=!test:~0,%maxlen%!
  200.     set test=%test:*_=%
  201.     set /a len=maxlen-(%test:#=1+%1)
  202.     endlocal &set %2=%len%
  203.     goto :eof
  204. :: plp626的时间差函数 时间跨度在1分钟内可调用之;用于测试一般bat运行时间
  205. :time_delta <beginTimeVar> <endTimeVar> <retVar> // code by plp626
  206.     setlocal
  207.     set ta=%1&set tb=%2
  208.     set /a "c=1!tb:~-5,2!!tb:~-2!-1!ta:~-5,2!!ta:~-2!,c+=-6000*(c>>31)"
  209.     if defined %3 set /a c+=!%3!
  210.     endlocal&set %3=%c%
  211.     goto:eof
复制代码

TOP

本帖最后由 523066680 于 2019-1-13 19:07 编辑

回复 33# SQYSQYSQY

是的哟,if 在某些情况下会进行字符串对比。bbbbbbbbbbbbbbb gtr aaaaaaaaaaaaaaa 是成立的,换成数字,只要长度相同就会得到对的结果

42行是我用来校验结果是否正确的,调用 Perl 显示对应的开根结果。要正确执行需要安装 Perl 环境。
http://strawberryperl.com/releases.html 这玩意儿好像都不自带环境配置,装完还要自己把perl.exe执行路径添加到PATH中

对应的校验语句是临时注释掉的:
rem call :check_first %1 !precision!

TOP

本帖最后由 523066680 于 2019-1-24 16:32 编辑

回复 35# SQYSQYSQY

    查阅 ASCII 字符表,如果想学习更多关于字符编码,建议:学习 python,用python去读写各种网页文件。
关键词:Encode, Decode, Unicode, UTF8, UTF16, GBK, CP936

用其他语言显示数字符号对应的ASCII码。
  1. >perl -e "grep {printf qq('%d' - %d\n), $_, ord($_) } (0..9)"
  2. '0' - 48
  3. '1' - 49
  4. '2' - 50
  5. '3' - 51
  6. '4' - 52
  7. '5' - 53
  8. '6' - 54
  9. '7' - 55
  10. '8' - 56
  11. '9' - 57
复制代码

TOP

回复 38# SQYSQYSQY

非常短(难道是受到了27楼小朋友的刺激?他短任他短,清风拂山冈。他横任他横,明月照大江)。可能有小细节需要完善:
1.414找不到操作数。
999999999999999999

    不打算先把大数和浮点数做进去再精简吗。

浮点数版本,效率老样子,代码不打算优化了,要也用Perl,或者换C艹
1

评分人数

    • SQYSQYSQY: 浮点数也能计算啊。(其实就是对整数计算, ...技术 + 1

TOP

本帖最后由 523066680 于 2019-1-17 20:23 编辑

回复 40# SQYSQYSQY

      你这起跑线多好,我那会儿没零钱,严重的时候一周只有1元上一个小时网吧,要做点什么得在稿纸上写好,不然刷的一小时没了。

空间换时间/时间换空间
查表就是为了"空间换时间",几百个上千元素的数组查询都会慢的也就是批处理了,不如趁早换用自由度高的语言。

那个例子可以用函数表达,x^2,和 5*x 赛跑,一开始 5*x 比较快,后来 x^2 …… 这俩太陡了,应该有更合适的曲线,暂时先这样


浮点数版本补充在39楼。

TOP

本帖最后由 523066680 于 2019-1-17 23:14 编辑

1月17日
补充 —— 是楼层穿越问题。此信息PASS

TOP

本帖最后由 523066680 于 2019-1-18 11:40 编辑

回复 37# SQYSQYSQY

      关于自由度,我不说数独,你用批处理做一做猜数字游戏的搜索树就知道了,但凡跟递归、多重镶嵌数据结构有关的东西,用批处理都不划算。
http://bbs.bathome.net/thread-49436-1-2.html
(非杠,我就是觉得你学其他语言,起跑线就会有一个质的飞跃。也不推荐Perl,现在的主流推荐就是Python,Java,C)

百度百科的描述
https://baike.baidu.com/item/%E7 ... 97/83200?fr=aladdin

举一个简单的例子:
我要生成50个随机字符,内容可以是数字和小写字母;将字符串拆割重组、输出显示整个数组:
  1. use Modern::Perl;
  2. # 生成 50 个随机字符,范围是数字和小写字母
  3. my @arr = map { ('0'..'9', 'a'..'z')[rand(36)] } (1 .. 50 );
  4. my $s = "bathomenet";
  5. say join(",", @arr);
  6. say join(",", split //, $s);
  7. print length($s);
复制代码
输出
  1. j,x,c,z,7,k,u,p,d,w,u,k,d,0,s,9,l,a,7,n,i,9,h,s,l,5,1,q,d,m,j,i,p,m,3,x,4,h,s,d,r,s,3,q,w,i,d,z,f,y
  2. b,a,t,h,o,m,e,n,e,t
  3. 10
复制代码
这些在 python ruby perl 里面都是常规操作,字符串长度允许范围几乎就是对应内存的剩余空间。

批处理可以做到,但是会很烦,vbs同样烦,js、Powershell应该好写

回复 39# SQYSQYSQY

      我也是业余的,工作和IT不相关。可以预见的是你的路线可以是 x^2。
而我这种职业不相关的,路线基本就只能是平缓直线甚至是上下波动了

TOP

回复 40# SQYSQYSQY

    先写一个测试模块,发布前测试过会比较妥。更新后的代码测试26,37,50,65,82不通过。更新前测试通过(精确到80位)。
  1. for /l %%a in (1,1, 100%或者其他数字%) do ( call :开根函数 )
  2. exit/b
  3. :开根函数
  4. setlocal
  5. endlocal& goto :eof
复制代码

TOP

本帖最后由 523066680 于 2019-1-24 21:48 编辑

回复 28# search_Sudoku

    将手算法原样撸了一个C++初始版本,未优化,1W精度(含输出)0.9秒。

返回来看gmp库的说明,这理论好深(估计happy能看懂,我只能看看概述)
Introduction
The current asymptotically fastest known method to compute the square-root of a n-word number is using Fast Fourier Transform (FFT) multiplication and Newton'smethod,
with a complexity of 5M(n)' [1, p. 155].2 The algorithm presented here is basedon Burnikel-Ziegler Karatsuba division [2].
这里面用到 快速傅里叶变换乘法、牛顿迭代法,以及 Burnikel-Ziegler Karatsuba 除法。

牛顿迭代法的精度增长很快(差不多翻倍增长),需要用到大数除法。实践起来,具体的精度不太好确定,因此迭代到一定次数后,除法的精度取舍也是个问题,
我也发现引用了牛顿迭代法的方案大多是给出一个预判的精度,
比如 gmp 中设置 mpf_set_default_prec (10000);  开根后 gmp_printf ("Square root: %.10000Ff\n\n", sq_out);
实际有效数字只有3068位,末尾由大量的0填充。

几种接口效率对比(i7 8核 4GHz)
Perl bignum 模块,1W精度 耗时4.5s
Python Decimal 模块,10W精度 耗时 1.0s
C++ 使用 GMP库,100W有效精度,含输出,1.5秒;不含输出,0.2秒
                           1000W位有效精度,不含输出, 2.5秒。

libgmp太凶悍

C++ 测试GMP速度以及判断有效精度的代码:
  1. #include <iostream>
  2. #include <sstream>
  3. #include <iomanip>
  4. #include <chrono>
  5. #include "gmpxx.h"
  6. #include "gmp.h"
  7. using namespace std;
  8. int main(int argc, char *argv[] )
  9. {
  10.     ostringstream os;
  11.     mpf_class n(2, 10), r(1, 3600000);
  12.     auto start = chrono::system_clock::now();
  13.         r = sqrt(n);
  14.         os << setprecision(10000000) << r;
  15.     auto end = chrono::system_clock::now();
  16.     chrono::duration<double> diff = end-start;
  17.     cout << "Actual length of r: " << os.str().length() << endl;
  18.     cout << "Time Used: " << setprecision(3) << diff.count() << " s" << endl;
  19.     return 0;
  20. }
复制代码
Actual length of r: 1083710
Time Used: 0.561 s (这里包含 os << r 产生的时间)

GNU MP 库中用到的算法和理论,完整版:
《Modern Computer Arithmetic》
http://maths.anu.edu.au/~brent/pd/mca-cup-0.5.9.pdf

TOP

返回列表