楼主: tree_new_bee

Euler Project 挨个做- 之二 (Q51-Q78)

[复制链接]
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
181#
发表于 2012-2-9 00:49 | 只看该作者
我写了个PLSQL不知道对不对?

DECLARE

   lv_limit NUMBER := 100;
   TYPE t_num IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
   lv_num t_num;
   lv_cnt   t_num;
   lv_cnt2  t_num;
   lv_sum NUMBER ;
   lv_ptr NUMBER;
BEGIN
   lv_cnt(0):=1;
   FOR i IN 1..lv_limit LOOP
       lv_num(i):=i;
   END LOOP;
   
   FOR i IN REVERSE 1..lv_num.COUNT LOOP
       lv_sum := lv_num(i);
       lv_cnt2:=lv_cnt;
       WHILE lv_sum<=lv_limit LOOP
             lv_ptr := 0;
             WHILE lv_ptr IS NOT NULL LOOP
                   EXIT WHEN lv_ptr+lv_sum>lv_limit;
                   IF NOT lv_cnt.EXISTS(lv_ptr+lv_sum) THEN
                      lv_cnt(lv_ptr+lv_sum):= lv_cnt2(lv_ptr);
                   ELSE
                      lv_cnt(lv_ptr+lv_sum):= lv_cnt(lv_ptr+lv_sum)+lv_cnt2(lv_ptr);
                   END IF;
                   lv_ptr := lv_cnt2.NEXT(lv_ptr);
             END LOOP;
             lv_sum := lv_sum + lv_num(i);
       END LOOP;
   END LOOP;
   DBMS_OUTPUT.PUT_LINE(lv_cnt(lv_limit)-1);
END;
/

190569291

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.02

使用道具 举报

回复
论坛徽章:
10
CTO参与奖
日期:2009-02-20 09:44:20ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30迷宫蛋
日期:2012-05-07 10:55:58茶鸡蛋
日期:2012-04-19 16:08:262012新春纪念徽章
日期:2012-01-04 11:54:462011新春纪念徽章
日期:2011-01-04 10:24:02数据库板块每日发贴之星
日期:2010-12-19 01:01:02数据库板块每日发贴之星
日期:2010-12-13 01:01:012009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2014-02-08 06:00:12
182#
 楼主| 发表于 2012-2-9 13:47 | 只看该作者
本帖最后由 tree_new_bee 于 2012-2-9 14:05 编辑
newkid 发表于 2012-2-9 00:49
我写了个PLSQL不知道对不对?

DECLARE

测试了几个,都没问题。

用你的结果去解答,是对的。


但程序没看明白, 能不能把思路说说?

使用道具 举报

回复
论坛徽章:
10
CTO参与奖
日期:2009-02-20 09:44:20ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30迷宫蛋
日期:2012-05-07 10:55:58茶鸡蛋
日期:2012-04-19 16:08:262012新春纪念徽章
日期:2012-01-04 11:54:462011新春纪念徽章
日期:2011-01-04 10:24:02数据库板块每日发贴之星
日期:2010-12-19 01:01:02数据库板块每日发贴之星
日期:2010-12-13 01:01:012009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2014-02-08 06:00:12
183#
 楼主| 发表于 2012-2-9 20:07 | 只看该作者
newkid 发表于 2012-2-9 00:49
我写了个PLSQL不知道对不对?

DECLARE

看到你的结果,就知道我179#的方法根本行不通了。
处理1.9亿个字符串,根本不可能在可接受的时间做到。

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
184#
发表于 2012-2-9 23:45 | 只看该作者
这是递归的思路,假设1~N个数,在N加入之前,F(1),F(2),...F(LIMIT) 表示和为1,2,...LIMIT 分解为加数1~N-1的方法的种数(F(0)恒为1),那么把N加入后,其可能的和就是:N+1,N+2,N+3,... 2N+1,2N+3,2N+3....3N+1,3N+2,3N+3.... LIMIT,这里面有重复的。
此处1N,2N,3N,...等等体现在下面WHILE循环的lv_sum变量。
F()的值体现在代码中的lv_cnt2数组。
随便拿一个为例,比如 3N+K 这个和,它必须包含加数3N, 所以拆法种数等于和为K的拆法种数(每种上面加3N而已)。
如果这个和是首次出现,拆法个数就相当于和为K的拆法的个数F(K)。
如果非首次出现(这个和可能在2N或1N的时候已经算过),那么现在就是在原来基础上增加F(K)。 

lv_cnt保存的是在lv_cnt2基础上增加N的各种拆法的个数,把所有N的组合算完了,再算N+1时这个结果就转移到lv_cnt2.

因为这些和肯定是连续的,昨天的第二个WHILE可以改为FOR LOOP.

DECLARE
   lv_limit NUMBER := 100;
   TYPE t_num IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
   lv_cnt   t_num;
   lv_cnt2  t_num;
   lv_sum NUMBER ;
BEGIN
   lv_cnt(0):=1;  ---------- 和为0肯定只有一种拆法
   
   FOR i IN 1..lv_limit LOOP
       lv_sum := i;
       lv_cnt2:=lv_cnt;
       WHILE lv_sum<=lv_limit LOOP
             FOR lv_ptr IN 0..lv_cnt2.COUNT-1 LOOP
                   EXIT WHEN lv_ptr+lv_sum>lv_limit;
                   IF NOT lv_cnt.EXISTS(lv_ptr+lv_sum) THEN ----- 这个和首次出现
                      lv_cnt(lv_ptr+lv_sum):= lv_cnt2(lv_ptr);
                   ELSE
                      lv_cnt(lv_ptr+lv_sum):= lv_cnt(lv_ptr+lv_sum)+lv_cnt2(lv_ptr);
                   END IF;
             END LOOP;
             lv_sum := lv_sum + i;
       END LOOP;
   END LOOP;
   DBMS_OUTPUT.PUT_LINE(lv_cnt(lv_limit)-1);  ---------- 最后输出的是F(100), 因为至少必须有两个加数,100自身不算所以减去1
END;
/

使用道具 举报

回复
论坛徽章:
10
CTO参与奖
日期:2009-02-20 09:44:20ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30迷宫蛋
日期:2012-05-07 10:55:58茶鸡蛋
日期:2012-04-19 16:08:262012新春纪念徽章
日期:2012-01-04 11:54:462011新春纪念徽章
日期:2011-01-04 10:24:02数据库板块每日发贴之星
日期:2010-12-19 01:01:02数据库板块每日发贴之星
日期:2010-12-13 01:01:012009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2014-02-08 06:00:12
185#
 楼主| 发表于 2012-2-10 15:08 | 只看该作者
本帖最后由 tree_new_bee 于 2012-2-10 15:55 编辑

看了半天,还是半懂。
还是自己琢磨了一下算法:

假设和为m,组合中最大的数为n,这样的组合数记为f(m,n)个
题目中求的就是f(100,99)   (因为至少两个数,所以不能含有100)

对于f(m,n)可分三种情况:
m<n, 因为超过和数m的n不可能出现, 所以f(m,n) = f(m,m)
m=n, 因为含有n的组合只有一种,剩下的不含n的组合也就是最大数为n-1的组合, 因此f(m,n) = 1+ f(m,n-1)
m>n, 分为含n和不含n两种情况考虑:含n的情况, 余下的数的和为m-n, 就是f(m-n, n);  不含n的情况,也就是最大数为n-1的组合,即f(m, n-1)
         所以f(m,n) = f(m-n, n) +  f(m, n-1)

有了这个,其实用excel都可以很轻松的算出任意f(m,n)


使用道具 举报

回复
论坛徽章:
10
CTO参与奖
日期:2009-02-20 09:44:20ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30迷宫蛋
日期:2012-05-07 10:55:58茶鸡蛋
日期:2012-04-19 16:08:262012新春纪念徽章
日期:2012-01-04 11:54:462011新春纪念徽章
日期:2011-01-04 10:24:02数据库板块每日发贴之星
日期:2010-12-19 01:01:02数据库板块每日发贴之星
日期:2010-12-13 01:01:012009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2014-02-08 06:00:12
186#
 楼主| 发表于 2012-2-10 15:10 | 只看该作者
上面算法的pl/sql实现:
  1. create or replace procedure Q76(arg number default 100) is
  2. type t_nlist is table of number index by pls_integer;
  3. type t_nlistlist is table of t_nlist index by pls_integer;

  4. nlist t_nlistlist;
  5. sm number;
  6. begin
  7. for i in 1..arg loop
  8.     nlist(1)(i) := 1;
  9.     nlist(i)(1) := 1;
  10. end loop;
  11.   
  12. for i in 2..arg loop
  13.   for j in 2..arg loop
  14.       if j<i then
  15.         nlist(i)(j) := nlist(i-j)(j) + nlist(i)(j-1);
  16.       elsif j=i then
  17.         nlist(i)(j) := nlist(i)(j-1) + 1;
  18.       elsif j>i then
  19.         nlist(i)(j) :=nlist(i)(j-1);
  20.       end if;
  21.   end loop;

  22. end loop;
  23. dbms_output.put_line(nlist(arg)(arg-1));
  24. end Q76;
复制代码
非常快!

SQL> exec q76(100);

190569291

PL/SQL procedure successfully completed

Executed in 0 seconds

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
187#
发表于 2012-2-10 15:45 | 只看该作者
tree_new_bee 发表于 2012-2-10 15:10
上面算法的pl/sql实现:非常快!

SQL> exec q76(100);

算法是王

使用道具 举报

回复
论坛徽章:
10
CTO参与奖
日期:2009-02-20 09:44:20ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30迷宫蛋
日期:2012-05-07 10:55:58茶鸡蛋
日期:2012-04-19 16:08:262012新春纪念徽章
日期:2012-01-04 11:54:462011新春纪念徽章
日期:2011-01-04 10:24:02数据库板块每日发贴之星
日期:2010-12-19 01:01:02数据库板块每日发贴之星
日期:2010-12-13 01:01:012009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2014-02-08 06:00:12
188#
 楼主| 发表于 2012-2-10 15:57 | 只看该作者
同样的算法用model语句来实现。
  1. with targ as (select 100 arg from dual)
  2. ,t as (select level d  from targ connect by level<=arg)
  3. ,t2 as (select tr.d r, tc.d c, 1  num from t tr, t tc)
  4. ,t3 as (select r,c, num
  5. from t2
  6. MODEL
  7.     DIMENSION BY (r,c)
  8.     MEASURES (num)
  9.     RULES update
  10.      (
  11.      num[any, any] order by r , c
  12.      = case when cv(r)>1 and cv(c)>1 and cv(r)<cv(c) then num[cv(r),cv(r)]
  13.             when cv(r)>1 and cv(c)>1 and cv(r)=cv(c) then 1+num[cv(r),cv(c)-1]
  14.             when cv(r)>1 and cv(c)>1 and cv(r)>cv(c) then num[cv(r)-cv(c),cv(c)]+num[cv(r),cv(c)-1]  
  15.             else  num[cv(r),cv(c)] end
  16. ) order by r , c)
  17. select r,c,num from t3,targ where r=arg and c=arg-1
复制代码
R          C        NUM
---------- ---------- ----------
       100         99  190569291

Executed in 0.078 seconds

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
189#
发表于 2012-2-10 23:14 | 只看该作者
你的不含n变成f(m-n,n)的思路比我要简洁,我的思路是考虑f(m,n-1)如何变成f(m,n)。
MODEL的简洁真是无敌,但不知道思路就看不懂了。

使用道具 举报

回复
论坛徽章:
10
CTO参与奖
日期:2009-02-20 09:44:20ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30迷宫蛋
日期:2012-05-07 10:55:58茶鸡蛋
日期:2012-04-19 16:08:262012新春纪念徽章
日期:2012-01-04 11:54:462011新春纪念徽章
日期:2011-01-04 10:24:02数据库板块每日发贴之星
日期:2010-12-19 01:01:02数据库板块每日发贴之星
日期:2010-12-13 01:01:012009日食纪念
日期:2009-07-22 09:30:00优秀写手
日期:2014-02-08 06:00:12
190#
 楼主| 发表于 2012-2-10 23:48 | 只看该作者
同样的思路,做Q31 ( http://www.itpub.net/forum.php?m ... 10&pid=17000140 )
比较起来,以前用的方法无论从简洁性还是从运行效率来讲,都太笨拙了。
  1. create or replace procedure Q31new(arg number default 200) is
  2. type t_nlist is table of number index by pls_integer;
  3. type t_nlistlist is table of t_nlist index by pls_integer;

  4. nlist t_nlistlist;
  5. coins t_nlist;
  6. sm number;
  7. begin

  8.   coins(1) := 1;
  9.   coins(2) := 2;
  10.   coins(3) := 5;
  11.   coins(4) := 10;
  12.   coins(5) := 20;
  13.   coins(6) := 50;
  14.   coins(7) := 100;
  15.   coins(8) := 200;

  16. for i in 1..arg loop
  17.     nlist(1)(i) := 1;
  18.     nlist(i)(1) := 1;
  19. end loop;
  20.   
  21. for i in 2..arg loop
  22.   for j in 2..8 loop
  23.       if coins(j)<i then
  24.         nlist(i)(coins(j)) := nlist(i-coins(j))(coins(j)) + nlist(i)(coins(j-1));
  25.       elsif coins(j)=i then
  26.         nlist(i)(coins(j)) := nlist(i)(coins(j-1)) + 1;
  27.       elsif coins(j)>i then
  28.         nlist(i)(coins(j)) :=nlist(i)(coins(j-1));
  29.       end if;
  30.   end loop;

  31. end loop;
  32. dbms_output.put_line(nlist(arg)(arg));
  33. end Q31new;
复制代码
SQL> exec q31new(200)

73682

PL/SQL procedure successfully completed

Executed in 0.016 seconds

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表