浏览 5138 次
锁定老帖子 主题:关于可重排列的面试题一道
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-10-20
最后修改:2010-10-20
http://www.iteye.com/topic/786239
问题帖见:lz 已经给出最简单的递归解法了: n 级台阶的走法 先走 1 步,问题就变成 n - 1 级台阶的走法 先走 2 步,问题就变成 n - 2 级台阶的走法 先走 3 步,问题就变成 n - 3 级台阶的走法 不过要优化算法,可以换换思路,把这个问题可以分解为两个子问题:
为什么这么算比较有效率呢?因为: 1. 可重复元素的组合的表示技巧:使用元素的个数而非相同元素的数组比较有效率。 2. 两个子问题都可以用动态规划快速减小搜索空间。 想清楚以后代码就非常简单了 …… 不用多说: # coding:utf-8 $size = 0 # 把 n个1, m个2,l个3 的所有排列附加到 s 的后面输出 def out s, n, m, l if (n == 0 and m == 0 and l == 0) $size += 1 puts s return end out "#{s}1", n - 1, m, l if n > 0 out "#{s}2", n, m - 1, l if m > 0 out "#{s}3", n, m, l - 1 if l > 0 end # 寻找和为 sum 的 1, 2, 3 的可重组合 def out_steps sum # sum = 3l + 2m + n 0.upto sum / 3 do |l| rest = sum - l * 3 0.upto rest / 2 do |m| n = rest - m * 2 out '', n, m, l end end end out_steps ARGV[0].to_i $stderr.puts "total:#$size" 下面测试一下 23 和 30: $ time ruby perm.rb 23 > res.txt total:755476 real 0m3.878s user 0m0.000s sys 0m0.016s $ time ruby perm.rb 30 > res.txt total:53798080 real 4m42.528s user 0m0.000s sys 0m0.030s 输出文件有 1G 多,占用内存保持在 2.5M 以下。 为了验证结果是否正确,想想最简单的递归动态规划算法,可以得到走法总数的快捷计算方法: # coding:utf-8 # 设 n 级台阶的走法为 f(n) 种,容易知道: # f(n) = f(n-1) + f(n-2) + f(n-3) def f n a = 1, 2, 4, 7 4.upto n do a << (a[-1] + a[-2] + a[-3]) end return a[n - 1] end puts f 23 #=> 755476 puts f 30 #=> 53798080 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-10-20
ruby很简洁。。
|
|
返回顶楼 | |
发表时间:2010-10-20
1、用循环不用递归,降低内存。
2、用/3和/2,限制3和2出现的次数,降低循环次数。 |
|
返回顶楼 | |
发表时间:2010-10-20
kimmking 写道 ruby很简洁。。
我要说明一下的是,这是一个很好的方法。但并不代表ruby就简洁,java的封装性很好,如果只是要实现如上面功能的代码,java倒也可以做到很简洁。 |
|
返回顶楼 | |
发表时间:2010-11-05
最后修改:2010-11-05
我觉得只要用树来展开一下就可以了,而且更简洁: import java.util.Stack; public class StepCompositor { private Stack<String> steps = new Stack<String>(); public void printSteps(int n) { if (n == 1) { printStack("1"); return; } else if (n == 2) { printStack("1 1"); printStack("2"); return; } else if (n == 3) { printStack("1 1 1"); printStack("1 2"); printStack("2 1"); printStack("3"); return; } else if (n <= 0) { return; } for (int i = 1; i <= 3; i++) { steps.push("" + i); //存放路径节点 printSteps(n - i); //展开节点 steps.pop(); } } private void printStack(String tail) { for (String s : steps) System.out.print(s + " "); System.out.println(tail); } public static void main(String[] args) { new StepCompositor().printSteps(5); } } 一共打印出了13种,测试了23步台阶的情况下效率还是很高的,在控制台打印的话开销会很大。 |
|
返回顶楼 | |
发表时间:2010-11-05
最后修改:2010-11-05
我在笔记本上试了一下用Java写的输出结果到硬盘上的代码,求30步台阶的时候也用了4分40秒左右,看来瓶颈都在硬盘了,看不出效率。我改了一下“打印”的代码: private String printStack(String tail) { String r = ""; for (String s : steps) r = s; r = tail; return r; } 求30步台阶用了75秒左右,不知道LZ的Ruby代码,如果不输出到硬盘上的话,大概得用了多长时间 我想了解一下Java递归的效率,我的机器是Core i3 330M的笔记本。 |
|
返回顶楼 | |
发表时间:2010-11-06
最后修改:2010-11-06
好吧,这里改进一下 ls 的算法,缓存到 15:
# coding: utf-8 # 缓存 1..3 Cache = [ [], %w[1], %w[11 2], %w[111 12 21 3] ].each {|a| a.each { |s| s << "\n" } } # 缓存 4..15 12.times { Cache << [1, 2, 3].flat_map {|i| Cache[-i].map {|s| "#{i}#{s}" } } } def print_steps s, n if ts = Cache[n] ts.each {|t| print s, t } return end 1.upto(3) {|i| print_steps "#{i}#{s}", n - i } end print_steps "", ARGV[0].to_i 结果 ================================ $time ruby steps.rb 30 > res.txt real 2m49.744s user 0m0.000s sys 0m0.015s 如果不输出,也就 7 秒左右,但是这个数字没意义 …… 如果写成 Haskell 代码的话就不用手动缓存了,可以更快一些。 |
|
返回顶楼 | |
发表时间:2010-11-06
效率不是 Ruby 的长处 …… 顺便,让你们看看什么是简洁的解法 ……
C = [ [], %w[1], %w[11 2], %w[111 12 21 3] ] N = ARGV[0].to_i 4.upto N do C << [1, 2, 3].flat_map {|i| C[-i].map {|s| "#{i}#{s}" } } end puts C[N] |
|
返回顶楼 | |
发表时间:2010-11-06
night_stalker 写道
效率不是 Ruby 的长处 …… 顺便,让你们看看什么是简洁的解法 ……
C = [ [], %w[1], %w[11 2], %w[111 12 21 3] ] N = ARGV[0].to_i 4.upto N do C << [1, 2, 3].flat_map {|i| C[-i].map {|s| "#{i}#{s}" } } end puts C[N] 好难看懂 |
|
返回顶楼 | |