楼主: 郭清明

[精华] 求一超难存储过程

[复制链接]
论坛徽章:
10
八级虎吧徽章
日期:2008-12-28 14:00:47祖国60周年纪念徽章
日期:2009-10-09 08:28:00生肖徽章2007版:兔
日期:2009-03-30 16:21:12生肖徽章2007版:牛
日期:2009-03-10 21:33:00生肖徽章2007版:龙
日期:2009-03-10 21:27:46生肖徽章2007版:龙
日期:2009-03-10 21:14:14生肖徽章2007版:龙
日期:2009-02-27 11:34:09授权会员
日期:2009-01-05 12:32:292009新春纪念徽章
日期:2009-01-04 14:52:282011新春纪念徽章
日期:2011-02-18 11:43:34
41#
发表于 2009-9-3 20:09 | 只看该作者
xuexi

使用道具 举报

回复
论坛徽章:
0
42#
 楼主| 发表于 2009-9-3 21:35 | 只看该作者
原帖由 郭清明 于 2009-9-3 19:54 发表
前辈,这道题目还有一个终极BT的需求,不知道能否实现:假设现在表里面只有这样3条记录,如下图所示:
truncate table t_money;
insert into t_money values(1,5);
insert into t_money values(2,7);
insert into t_money values(3,7);
id            amount     
----------------------
1        5
2        7
3        7
-----------------------

所求总金额是10,容差是1 ,当所选组合大于容差范围的时候,拆分当前的记录的金额,使得拆分的金额+之前所选的金额总数=所求金额数,算法举例:   5+7=12,  12-10=2   ,2>1 不满足容差范围,所以拆分7,7=5+2,所以,得到5+5=10,  序列组合为 1,2     其中序列号为2 的记录有2元作为余额(余额不保存,只保存已选取金额),因此最终的结果仍然只要返回5+5=10,满足要求的组合的序列号为1,2就可以了。
谢谢。


拆分的前提条件是:找遍了所有的数据都没有满足要求的时候才进行拆分。

使用道具 举报

回复
论坛徽章:
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
43#
发表于 2009-9-3 21:48 | 只看该作者
原帖由 郭清明 于 2009-9-3 21:35 发表


拆分的前提条件是:找遍了所有的数据都没有满足要求的时候才进行拆分。

多出来可以拆分,如果不足呢?

使用道具 举报

回复
论坛徽章:
0
44#
 楼主| 发表于 2009-9-3 22:41 | 只看该作者

回复 #43 newkid 的帖子

如果所有的数据的金额加起来都不足所求金额,就返回“无符合条件的组合”

使用道具 举报

回复
论坛徽章:
0
45#
 楼主| 发表于 2009-9-3 22:43 | 只看该作者
原帖由 郭清明 于 2009-9-3 22:41 发表
如果所有的数据的金额加起来都不足所求金额,就返回“无符合条件的组合”



在实际应用中,基本不存在不足的情况,只有多,没有少的。

使用道具 举报

回复
论坛徽章:
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
46#
发表于 2009-9-3 22:54 | 只看该作者
declare
   type test_t is table of vw_money%rowtype index by binary_integer;
   res test_t;
   l_row binary_integer:=1;
   sums number;
   cal varchar2(1000);
   seq varchar2(1000);
   j NUMBER;
   lv_result NUMBER;
   
   lv_target NUMBER := 10;     --- 目标
   lv_diff   NUMBER := 2;      --- 允许误差
   
   lv_cal VARCHAR2(1000);
   lv_seq VARCHAR2(1000);
   
   lv_close NUMBER;
   
begin

  SELECT *
  BULK COLLECT INTO res
  FROM vw_money;
  
  --dbms_output.put_line(res.count);
  lv_result := NULL;
  lv_close := NULL;
  for i in res.first .. 2**res.last-1 loop
      sums:=0;
      seq:='';
      cal:='';
      j:=1;
      WHILE j<=res.count AND res(j).pow<=i LOOP
       if (bitand(i,res(j).pow) <> 0) then  -- 2的幂事先算好了
         sums:=sums+res(j).amount;
         cal:=cal||res(j).amount||'+';
         seq:=seq||res(j).id||',';   
         IF sums - lv_target >= ABS(lv_result - lv_target) OR sums - lv_target>lv_diff THEN        ---- 已经有更接近的答案或误差超出范围
            EXIT;
         END IF;
       end if;
       j:=j+1;
      end loop;

      IF ABS(sums - lv_target)<=lv_diff AND (lv_result IS NULL OR ABS(sums - lv_target) < ABS(lv_result - lv_target)) THEN
         lv_result := sums;
         lv_cal := cal;
         lv_seq := seq;
      ELSIF lv_result IS NULL AND sums - lv_target>lv_diff AND (lv_close IS NULL OR sums<lv_close) THEN
         lv_close := sums;
         lv_cal := cal;
         lv_seq := seq;
      END IF;

      IF lv_result = lv_target THEN
         EXIT;
      END IF;

  end loop;

  IF lv_result IS NULL THEN
     IF lv_close IS NULL THEN
        dbms_output.put_line('不存在符合要求的答案');
     ELSE
        lv_cal:=substr(lv_cal,1,length(lv_cal)-1)||'-'||(lv_close-lv_target);
        lv_seq:=substr(lv_seq,1,length(lv_seq)-1);
        dbms_output.put_line('结果是:'||lv_cal||'='||lv_target);
        dbms_output.put_line('序列是:'||lv_seq);

     END IF;
  ELSE
     lv_cal:=substr(lv_cal,1,length(lv_cal)-1);
     lv_seq:=substr(lv_seq,1,length(lv_seq)-1);
     dbms_output.put_line('结果是:'||lv_cal||'='||lv_result);
     dbms_output.put_line('序列是:'||lv_seq);
  END IF;
  
end;
/

[ 本帖最后由 newkid 于 2009-9-3 22:56 编辑 ]

使用道具 举报

回复
论坛徽章:
21
红旗
日期:2013-09-30 15:26:01凯迪拉克
日期:2013-10-23 12:48:26比亚迪
日期:2013-11-01 09:19:01奔驰
日期:2013-12-13 09:27:30马上有对象
日期:2014-11-18 10:46:242015年新春福章
日期:2015-04-28 15:24:55慢羊羊
日期:2015-05-28 09:49:31
47#
发表于 2009-9-4 13:07 | 只看该作者
create view  tt0 as  
select   1 id,     2 amount from dual union all
select    2 ,     2 amount from dual union all
select    3 ,      3  amount from dual union all
select    4 ,      5  amount from dual union all
select    5 ,     2  amount from dual union all
select    6 ,     8  amount from dual union all
select    7 ,     1  amount from dual union all
select    8 ,     2  amount from dual union all
select    9 ,      3  amount from dual union all
select   10 ,     3  amount from dual  

;

create view  tt1 as  
select  * from tt0
;


create view  ttall as  
select  * from tt0
;



declare
  n number := 1;
  m number := 0;
  
  x_amount number:=700;--------需要计算的值
begin

  while (m = 0) loop
  
    execute immediate ('create or replace view ttall as
                     select tts.id ||''-''|| tt0.id id ,tts.amount+ tt0.amount amount
                     from
                       (select min(id ) id ,amount  
                        from   tt' || n || '
                        group by amount ) tts,tt0
                     where tts.amount<'|| x_amount);
                     
    execute immediate ('create or replace view tt' || to_char(n + 1) ||
                      ' as
                     select tts.id ||''-''|| tt0.id id ,tts.amount+ tt0.amount amount
                     from
                     (select min(id ) id ,amount  
                     from
                     tt' || n || '
                     group by amount ) tts,tt0
                      where tts.amount<'|| x_amount);
    n := n + 1;
    select count(1) into m from ttall where amount = x_amount;
  end loop;

  for cur in (select id from ttall where amount = x_amount) loop
    dbms_output.put_line(cur.id);
  end loop;

end;

[ 本帖最后由 grubbyoo 于 2009-9-5 09:18 编辑 ]

使用道具 举报

回复
论坛徽章:
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
48#
发表于 2009-9-4 22:06 | 只看该作者
原帖由 grubbyoo 于 2009-9-4 13:07 发表
create view  tt0 as  
select   1 id,     2 amount from dual union all
select    2 ,     2 amount from dual union all
select    3 ,      3  amount from dual union all
select    4 ,      5  amount from dual union all
select    5 ,     2  amount from dual union all
select    6 ,     8  amount from dual union all
select    7 ,     1  amount from dual union all
select    8 ,     2  amount from dual union all
select    9 ,      3  amount from dual union all
select   10 ,     3  amount from dual  

;

create view  tta1 as  
select  * from tt0
;


create view  ttall as  
select  * from tt0
;



declare
  n number := 1;
  m number := 0;
  
  x_amount number:=700;--------需要计算的值
begin

  while (m = 0) loop
  
    execute immediate ('create or replace view ttall as
                     select tts.id ||''-''|| tt0.id id ,tts.amount+ tt0.amount amount
                     from
                       (select min(id ) id ,amount  
                        from   tt' || n || '
                        group by amount ) tts,tt0
                     where tts.amount

您老又重出江湖了?等会研究一下你的代码。

使用道具 举报

回复
论坛徽章:
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
49#
发表于 2009-9-4 23:35 | 只看该作者
原帖由 grubbyoo 于 2009-9-4 13:07 发表
create view  tt0 as  
select   1 id,     2 amount from dual union all
select    2 ,     2 amount from dual union all
select    3 ,      3  amount from dual union all
.....

看了一下好像会死循环,我没敢试。
建视图的办法不好。你会产生一堆垃圾,而且每次循环不能利用前面的结果集。不如用临时表或嵌套表。
而且你的办法有可能一个ID出现多次,你没有仔细看需求吧?

使用道具 举报

回复
论坛徽章:
0
50#
 楼主| 发表于 2009-9-7 19:42 | 只看该作者

回复 #46 newkid 的帖子

前辈,select power(2,419) from dual;在 oracle10g里面会报numeric overflow 错误,言外之意是不是这个算法只适合去查询数据量小于419条的表,一旦大于418条,数据库就会报错是吧

for i in res.first .. 2**res.last-1 loop 这个循环相当于要去计算 power(2,419)是吗

我那个需求是不是必须要用背包算法搞啊,我实际的数据量起码上万条,莫非无解?

使用道具 举报

回复

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

本版积分规则 发表回复

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