@shellex说: 定个闹钟,然后,晚安

对付冗余计算…的那个途径

问题来自SICP.

该Sample描述了一个换硬币的问题,卡卡,就是偶们是小朋友的是哈哈,父亲大人/母亲大人可能给你一张大..大的钞票,出去打酱油..:D,花不完的,老板会找你钱。找多少钱是个思想斗争,于是就有了这个问题。

给予一定的硬币种类kinds-of-coins和一定的金额amount,使用这些硬币有多少种方式能组成这个金额呢?

很简单哈,所以就书上直接就是源码鸟(这里)。

可惜哈,这个代码实现的是一个树状递归,而树状递归的效率是非常的龌龊,不信你可以试试画一下amount=11, kinds-of-coins=5时的递归计算过程,这个树的很多枝枝丫丫都是一样de,骗取社会主义劳动成功,死人领着活人工资。

所以在脚注34里就提到一种对付这些枝枝丫丫的政策,所谓“记忆技术”(memoization)就是了。

脚注里面说得很呆,其实很简单,把已经算得的结果缓存起来,然后遇到同样的计算任务的时候拿出来用就好。练习3.27就是这么搞的,可惜scheme里面木有构造Cache的现成数据结构,要我实现dic,hash table,什么的我那么lazy的所以不愿意的。不过我的实现也不大优雅,memorize和被memorize的函数杂糅到一起了,python高手告诉我,有木有办法分开?

import time

def change_coins(money):
	first_denomination = {
		1:1,  2:5,
		3:10, 4:25,
		5:50,
	}
	def cc((amount, kinds_of_coins)):
		if amount == 0:
			return 1
		elif amount < 0 or kinds_of_coins == 0:
			return 0
		else:
			return 	cc((amount, kinds_of_coins - 1)) \
			+ cc((amount - first_denomination[kinds_of_coins], kinds_of_coins))

	return cc((money, 5))

def change_coins_memoization(money):
	first_denomination = {
		1:1,  2:5,
		3:10, 4:25,
		5:50,
	}
	cache = {}
	cc_memo = lambda x :memoize(cc) (x)
	def memoize(fun):
		def proc(k):
			if cache.has_key(k):
				return cache[k]
			else:
				x = fun(k)
				cache[k] = x
				return x
		return proc

	def cc((amount, kinds_of_coins)):
		if amount == 0:
			return 1
		elif amount < 0 or kinds_of_coins == 0:
			return 0
		else:
			return cc_memo((amount, kinds_of_coins - 1)) + \
				cc_memo((amount - first_denomination[kinds_of_coins], kinds_of_coins))

	return cc_memo((money, 5))

if __name__ == "__main__":
	money = int(raw_input())
	start = time.clock()
	print "change_coins return %s cost %fs"  % (change_coins(money), (time.clock()-start))

	start = time.clock()
	print "change_coins_memoization return %s cost %fs"  % (change_coins_memoization (money), (time.clock()-start))

python的那个栈的深度有限(讨厌得很,而且还不优化尾递归),输入300就差不多鸟,来看看memoization的小宇宙怎样爆发的:

change_coins return 9590 cost 1.100000s
change_coins_memoization return 9590 cost 0.000000s

做剪枝的时候也用到的。

  1. On August 4, 2008 at 12:31 pm
    vvoody :

    你的博客rss输出在g reader上十分难看,都挤在一起了。

    Notify
  2. On August 4, 2008 at 1:29 pm

    [quote comment="307"]你的博客rss输出在g reader上十分难看,都挤在一起了。[/quote]
    是吗..我不知道啊。在我这里貌似显示得很好的。我也是用google reader的。

    Notify
  3. On August 4, 2008 at 1:34 pm

    [quote comment="307"]你的博客rss输出在g reader上十分难看,都挤在一起了。[/quote]
    我试了一下Opera, 也木问题哈。

    Notify
  4. On August 5, 2008 at 3:26 pm
    vvoody :

    [quote comment="308"][quote comment="307"]你的博客rss输出在g reader上十分难看,都挤在一起了。[/quote]
    是吗..我不知道啊。在我这里貌似显示得很好的。我也是用google reader的。[/quote]

    见:
    http://vvoody.org/upload/u080805-sxnsx-rss.png

    Notify
  5. On August 5, 2008 at 7:00 pm

    [quote comment="310"][quote comment="308"][quote comment="307"]你的博客rss输出在g reader上十分难看,都挤在一起了。[/quote]
    是吗..我不知道啊。在我这里貌似显示得很好的。我也是用google reader的。[/quote]

    见:
    http://vvoody.org/upload/u080805-sxnsx-rss.png/quote
    奇怪了,同样是Opera 9.51,请看:
    http://paste.ubuntu.org.cn/8899

    对了,你的wp theme很漂亮

    Notify
  6. On August 6, 2008 at 12:13 pm
    vvoody :

    [quote comment="311"][quote comment="310"][quote comment="308"][quote comment="307"]你的博客rss输出在g reader上十分难看,都挤在一起了。[/quote]
    是吗..我不知道啊。在我这里貌似显示得很好的。我也是用google reader的。[/quote]

    见:
    http://vvoody.org/upload/u080805-sxnsx-rss.png/quote
    奇怪了,同样是Opera 9.51,请看:
    http://paste.ubuntu.org.cn/8899

    对了,你的wp theme很漂亮[/quote]

    http://www.sxnsx.com/feed/rss/ 这个rss输出有问题,其他几个http://www.sxnsx.com/feed/ 和着feedsky 的都正常。
    谢谢;-)

    Notify
  7. On August 6, 2008 at 12:14 pm
    vvoody :

    [quote comment="311"][quote comment="310"][quote comment="308"][quote comment="307"]你的博客rss输出在g reader上十分难看,都挤在一起了。[/quote]
    是吗..我不知道啊。在我这里貌似显示得很好的。我也是用google reader的。[/quote]

    见:
    http://vvoody.org/upload/u080805-sxnsx-rss.png/quote
    奇怪了,同样是Opera 9.51,请看:
    http://paste.ubuntu.org.cn/8899

    对了,你的wp theme很漂亮[/quote]

    除了http://www.sxnsx.com/feed/rss/,其他feed输出都正常。
    谢谢;-)

    Notify
  8. On August 6, 2008 at 12:15 pm
    vvoody :

    [quote comment="311"][quote comment="310"][quote comment="308"][quote comment="307"]你的博客rss输出在g reader上十分难看,都挤在一起了。[/quote]
    是吗..我不知道啊。在我这里貌似显示得很好的。我也是用google reader的。[/quote]

    见:
    http://vvoody.org/upload/u080805-sxnsx-rss.png/quote
    奇怪了,同样是Opera 9.51,请看:
    http://paste.ubuntu.org.cn/8899

    对了,你的wp theme很漂亮[/quote]

    除了http://www.sxnsx.com/feed/rss/,其他feed输出都正常。

    谢谢;-)

    Notify
  9. On August 6, 2008 at 12:16 pm
    vvoody :

    为啥留不了言

    Notify
  10. On August 6, 2008 at 12:17 pm
    vvoody :

    除了http://www.sxnsx.com/feed/rss/,其他feed输出都正常。

    谢谢;-)

    引用了怎么就不能留言了。

    Notify
  11. On August 6, 2008 at 12:20 pm

    [quote comment="316"]为啥留不了言[/quote]
    可以哈,你看…我。留俄了。

    使用feedsky的输出吧,虽然有点慢。谢谢你订阅我的blog。:)

    Notify
  12. On August 6, 2008 at 12:27 pm

    [quote comment="317"]除了http://www.sxnsx.com/feed/rss/,其他feed输出都正常。

    谢谢;-)

    引用了怎么就不能留言了。[/quote]
    你说得对。/rss/的这个有问题。

    留言不上的原因是。。。被Akismet和谐了。不好意思。

    Notify
  13. On August 6, 2008 at 4:22 pm
    山猫 :

    python 就用 yeld 弄生成器 + channel 消息代替要栈的普通函数啦~

    Notify
  14. On August 6, 2008 at 4:45 pm

    [quote comment="321"]python 就用 yeld 弄生成器 + channel 消息代替要栈的普通函数啦~[/quote]
    来,来,写一个我来学习学习

    Notify
  15. On August 26, 2008 at 5:41 pm
    Shell.E.Xu :

    我搞不懂的是,明明写了一个装饰器,为啥还问杂糅的问题.
    def memoiza(fun):
    cache = {}
    def proc ( *arg ):
    if cache.has_key(arg):
    return cache[arg]
    else:
    x = fun( *arg )
    cache[arg] = x
    return x
    return proc

    def decorator_change_coins(money):
    first_denomination = {
    1:1, 2:5,
    3:10, 4:25,
    5:50,
    }
    @memoiza
    def cc(amount, kinds_of_coins):
    if amount == 0:
    return 1
    elif amount < 0 or kinds_of_coins == 0:
    return 0
    else:
    return cc(amount, kinds_of_coins – 1) \
    + cc(amount – first_denomination[kinds_of_coins], kinds_of_coins)
    return cc(money, 5)
    另外,python和C的效率真没法比啊.C下面是这样的.
    300的计算结果
    kind of coins: 9590
    times:62
    kind of coins: 9590
    times:46
    1000的计算结果
    kind of coins: 801451
    times:15953
    kind of coins: 801451
    times:11000
    第一个是不缓存,第二个是缓存,似乎是因为C下面的Invoke动作耗时非常小.

    Notify
  16. On August 26, 2008 at 5:46 pm
    Shell.E.Xu :

    娘的,贴上去没格式了,凑合看看吧.
    def memoiza(fun):
    cache = {}
    def proc ( *arg ):
    if cache.has_key(arg):
    return cache[arg]
    else:
    x = fun( *arg )
    cache[arg] = x
    return x
    return proc

    def decorator_change_coins(money):
    first_denomination = {
    1:1, 2:5,
    3:10, 4:25,
    5:50,
    }
    @memoiza
    def cc(amount, kinds_of_coins):
    if amount == 0:
    return 1
    elif amount < 0 or kinds_of_coins == 0:
    return 0
    else:
    return cc(amount, kinds_of_coins – 1) \
    + cc(amount – first_denomination[kinds_of_coins], kinds_of_coins)
    return cc(money, 5)

    Notify
  17. On August 26, 2008 at 9:22 pm
    shellex :

    [quote comment="385"]娘的,贴上去没格式了,凑合看看吧.
    def memoiza(fun):
    cache = {}
    def proc ( *arg ):
    if cache.has_key(arg):
    return cache[arg]…
    [/quote]
    Haha, 谢谢, 我也学会了Python的包装器语法. 很棒.

    Notify
  18. On August 26, 2008 at 9:24 pm
    shellex :

    [quote comment="385"]娘的,贴上去没格式了,凑合看看吧.
    def memoiza(fun):
    cache = {}

    [/quote]
    PS: 您的名字….是随手起的吗

    Notify
  19. On August 27, 2008 at 6:24 pm
    Shell.E.Xu :

    随手起也不会随手一个匹配的邮箱吧~~
    我用这个名字已经10多年了。
    所以头次跑来像见鬼一样,我也用python写项目,在看SICP。
    BTW,更吊诡的是,我在网上看到还有一个品味差不多的shell,不过就不是EX就是啦。

    Notify
  20. On August 27, 2008 at 10:20 pm
    Shell.E.Xu :

    http://shell909090.spaces.live.com/blog/cns!7AD2FCE74833C21B!1854.entry
    可以看看这个

    Notify
  21. On August 31, 2008 at 3:26 pm

    [quote comment="392"]http://shell909090.spaces.live.com/blog/cns!7AD2FCE74833C21B!1854.entry
    可以看看这个[/quote]

    好, 订阅了。
    其实我是newbie,上文你说的方面都是。
    我用这个名字也有5、6年了,呵呵。

    Notify
  22. On July 21, 2009 at 11:01 am

    这个找零钱问题能写出循环版本么?SICP上只是提了一句,但没有给出答案,我试过一段时间,没搞定

    Notify
  23. On July 22, 2009 at 5:32 pm

    @pf_miles,
    嗯。我翻翻以前的代码,我也没写。

    Notify

Leave a Reply