论坛首页 编程语言技术论坛

关于可重排列的面试题一道

浏览 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, 3 的和为 N 的一个可重组合
  • 输出这个组合的所有排列

为什么这么算比较有效率呢?因为:
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

   发表时间:2010-10-20  
ruby很简洁。。
0 请登录后投票
   发表时间:2010-10-20  
1、用循环不用递归,降低内存。
2、用/3和/2,限制3和2出现的次数,降低循环次数。
1 请登录后投票
   发表时间:2010-10-20  
kimmking 写道
ruby很简洁。。

我要说明一下的是,这是一个很好的方法。但并不代表ruby就简洁,java的封装性很好,如果只是要实现如上面功能的代码,java倒也可以做到很简洁。
1 请登录后投票
   发表时间: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步台阶的情况下效率还是很高的,在控制台打印的话开销会很大。

0 请登录后投票
   发表时间: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的笔记本。

0 请登录后投票
   发表时间: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 代码的话就不用手动缓存了,可以更快一些。
0 请登录后投票
   发表时间: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]

0 请登录后投票
   发表时间: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]

好难看懂

0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics