楼主: tree_new_bee

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

[复制链接]
论坛徽章:
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
31#
 楼主| 发表于 2010-12-30 22:11 | 只看该作者
原帖由 newkid 于 2010-12-30 22:05 发表
等级作为排序第一列,第二列就直接比牌好了,把牌先按大小排序。比如 34567 用76543表示。


看的出来你没玩过, 为了便于你理解, 在楼上的帖子里,我加了一些例子。

“第二列就直接比牌好了“, 这个是不够的。  Ak553 < QJ662, 同样牌型, 但并不是按照排序后的大小比就行。
除非,先排序成 55AK3 vs 66QJ2
但这样的排序本身就比较麻烦。

使用道具 举报

回复
论坛徽章:
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
32#
 楼主| 发表于 2010-12-30 23:26 | 只看该作者
原帖由 tree_new_bee 于 2010-12-30 22:11 发表

除非,先排序成 55AK3 vs 66QJ2
但这样的排序本身就比较麻烦。


说完这段话, 受到了启发:
就用 牌型+这样的排序组合作为一手牌的综合分值。

比如:66QJ2只有一对, 归于牌型'2',  表示为 2 6 6 C B 2    ( CB分别代表16进制的12和11)

使用道具 举报

回复
论坛徽章:
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
33#
 楼主| 发表于 2010-12-30 23:46 | 只看该作者
根据上面的思路, 写了一段异常复杂的PLSQL代码:

create or replace function poker_rank(pk varchar2) return varchar2 is
  type t_poker is record (  -- 一张牌
    suit varchar2(2),   -- 花色
    val varchar2(2)     -- 牌点,用16进制数表示.
    );
  type t_poker_list is table of t_poker;
  
  hand t_poker_list;
  tmppoker t_poker;
  str varchar2(10);
  flush boolean;
  straight boolean;
  cnt1 integer;
  cnt2 integer;
  num1 varchar2(2);
  num2 varchar2(2);
  cnt1stop boolean;
  cnt2stop boolean;
  begin
    hand := new t_poker_list();
    for i in 1..5 loop
      hand.extend;
      hand(i).suit := substr(pk,i*3-1,1);
      str:=substr(pk, i*3-2,1);
      hand(i).val := translate(str, '23456789TJQKA', '23456789ABCDE');
    end loop;  

    for i in 2..5 loop  -- 从大到小排序 bubule sort
      for j in reverse i..5  loop
        if hand(j).val > hand(j-1).val then
          tmppoker := hand(j);
          hand(j) := hand(j-1);
          hand(j-1) := tmppoker;
        end if;
       end loop;
    end loop;  

    flush:= true;  --同花
    straight:=true;  -- 顺子   
    cnt1 := 1;  -- 第一个重复牌的数目, num1放相应的点数
    cnt2 := 1;  -- 第二个重复牌的数目, num2放相应的点数
    cnt1stop:=false;
    cnt2stop:=false;
    for i in 2..5 loop  -- 找重复牌
        if to_number(hand(i).val,'X') <> to_number(hand(i-1).val,'X')+1 then straight:=false; end if;
        if hand(i).suit <> hand(i-1).suit then flush:=false; end if;
        
        if not cnt1stop then
           if hand(i).val = hand(i-1).val then
            cnt1:=cnt1+1;
           else
             if cnt1>1 then cnt1stop:=true; num1:= hand(i-1).val; end if;
           end if;
          if i=5 and not cnt1stop and cnt1>1 then num1:=hand(i).val; end if;
        else
          if hand(i).val = hand(i-1).val then
            cnt2:=cnt2+1;
          else
            if cnt2>1 then cnt2stop:=true; num2:=hand(i-1).val; end if;
          end if;
          if i=5 and not cnt2stop and  cnt2>1 then num2:=hand(i).val; end if;
        end if;
            
    end loop;  
    -- 给出牌型分
    str:= case when flush and straight then '9'
               when flush then '6'
               when straight then '5'
               when greatest(cnt1,cnt2)=4 then '8'
               when greatest(cnt1,cnt2)=3 and least(cnt1,cnt2)=2 then '7'
               when greatest(cnt1,cnt2)=3 then '4'
               when cnt1=2 and cnt2=2 then '3'
               when cnt1=2 then '2'
               else '0'   
               end  ;
                  
    if not flush then     -- 同花就不再比重复牌, 只比大小(同花顺其实也相当于比大小)         
      -- 将重复多的牌放前面。
      if cnt1>= cnt2 and cnt1>1 then
        str:= str|| lpad(num1, cnt1, num1);
        if cnt2>1 then  
           str:= str||lpad(num2, cnt2, num2);
        end if;
      end if;        

      if cnt2> cnt1 then
        str:= str||lpad(num2, cnt2, num2);
        str:= str||lpad(num1, cnt1, num1);
      end if;   
    end if;   
      
    --整理出最终的牌型+牌序
    for i in 1..5 loop
      if length(str)=i or instr(substr(str,2), hand(i).val)=0 then
        str:=str|| hand(i).val;
      end if;
   end loop;
   --dbms_output.put_line(str);
  return str;

  end;


然后调用此函数即可:
  
--select p1, p2, poker_rank(p1) rk1, poker_rank(p2) rk2
select count(*)
from euler_poker
where poker_rank(p1) >poker_rank(p2)

  COUNT(*)
----------
       379

Executed in 0.156 seconds


可惜的是, 这个结果似乎不是正确答案。 也就是说上面的函数还是有问题。

[ 本帖最后由 tree_new_bee 于 2010-12-31 00: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
34#
发表于 2010-12-31 00:34 | 只看该作者
数据上传一下?用纯SQL比较好玩,先转成多行,再用分析函数排序,COUNT等等。

使用道具 举报

回复
论坛徽章:
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
35#
发表于 2010-12-31 07:58 | 只看该作者
外部数据和题目

Project Euler1_309.rar

782.19 KB, 下载次数: 21

prj_euler_py.rar

205.62 KB, 下载次数: 22

prjeuler.rar

69.74 KB, 下载次数: 19

使用道具 举报

回复
论坛徽章:
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
36#
 楼主| 发表于 2010-12-31 10:25 | 只看该作者
原帖由 newkid 于 2010-12-31 00:34 发表
数据上传一下?用纯SQL比较好玩,先转成多行,再用分析函数排序,COUNT等等。


为了避免再牵涉用文件数据的麻烦, 我给你导出成sql插入脚本。


其实有可能用sql反倒简单。 像我的代码中的排序什么的很折腾, 而用sql的话这些问题几乎不需要考虑。

只不过相对来说, 过程语言更容易让人理清整体逻辑。

[ 本帖最后由 tree_new_bee 于 2010-12-31 10:28 编辑 ]

euler_poker.sql

78.73 KB, 下载次数: 18

使用道具 举报

回复
论坛徽章:
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
37#
 楼主| 发表于 2010-12-31 11:41 | 只看该作者
按照newkid的想法, 写了一段sql, 只分析一手牌。

不过, 运行前小心!: 这段代码似乎会死循环。

with thand as (select 'AS 8C AC AD 8H' hand from dual)
,th1 as (select hand, translate(substr(hand,rownum*3-2,1),'23456789TJQKA', '23456789ABCDE') val, substr(hand,rownum*3-1,1) suit from thand connect by level<=5)
,tsort as (select hand, val, suit, count(*) over (partition by val) valcnt from th1 order by count(*) over (partition by val) desc)
,tsort2 as (select hand, rownum rn, val, suit, valcnt from tsort )
,tvalsort as (select hand, replace(sys_connect_by_path(val, '/'),'/','') valsort, replace(sys_connect_by_path(valcnt, '/'),'/','') valcnt from tsort2 where level=5 connect by rn=prior rn+1)
,tsuitcnt as (select hand, count(distinct suit) suitcnt from th1 group by hand)
select
case when suitcnt = 1 and valcnt='11111' and to_number(substr(valsort,1,1),'X') - to_number(substr(valsort,5,1),'X')=4 then '9'
     when suitcnt = 1 then '6'
     when valcnt='11111' and to_number(substr(valsort,1,1),'X') - to_number(substr(valsort,5,1),'X')=4 then '5'
     when valcnt='44441' then '8'
     when valcnt='33322' then '7'
     when valcnt='33311' then '4'
     when valcnt='22221' then '3'
     when valcnt='22111' then '2'
     else '0'
     end  || valsort
from tvalsort, tsuitcnt where tvalsort.hand = tsuitcnt.hand


单独查询tvalsort和tsuitcnt 都不会有问题, 但两个一关联,就会死掉。
查系统日志似乎没发现有什么异常。

使用道具 举报

回复
论坛徽章:
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
38#
发表于 2010-12-31 13:09 | 只看该作者
写了个例子,一手牌的综合分数很容易从这些数据得出:

with thand as (select 1 as id, 'AS 8C AC AD 8H' hand from dual)
SELECT id
      ,MAX(hand2) hand2  ---- 按张数, 牌值排序后的牌
      ,MAX(suits) suits  ---- 同上排序后的花色
      ,COUNT(DISTINCT CASE WHEN d_cnt=1 THEN d END) cnt1  ---- 单张的牌数
      ,COUNT(DISTINCT CASE WHEN d_cnt=2 THEN d END) cnt2  ---- 几个对
      ,COUNT(DISTINCT CASE WHEN d_cnt=3 THEN d END) cnt3  ---- 三张
      ,COUNT(DISTINCT CASE WHEN d_cnt=4 THEN d END) cnt4  ---- 四张
      ,COUNT(DISTINCT g)                            g     ---- 如果=1则为顺
      ,COUNT(DISTINCT s)                            cnts  ---- 如果=1同花
  FROM (SELECT t.*
              ,REPLACE(wmsys.wm_concat(d) OVER(PARTITION BY id ORDER BY d_cnt DESC,d DESC),',') hand2 --- 同点数越多越靠前
              ,REPLACE(wmsys.wm_concat(s) OVER(PARTITION BY id ORDER BY d_cnt DESC,d DESC),',') suits
              ,TO_NUMBER(d,'X')-ROW_NUMBER() OVER(PARTITION BY id ORDER BY d) g
          FROM (SELECT id
                      ,d,s
                      ,COUNT(*) OVER(PARTITION BY id,d) AS d_cnt
                  FROM (SELECT id
                              ,hand
                              ,SUBSTR(COLUMN_VALUE,1,1) d
                              ,SUBSTR(COLUMN_VALUE,2,1) s
                         FROM (SELECT id,hand,TRANSLATE(hand,'TJQKA','ABCDE') h FROM thand
                              )
                             ,TABLE(SYS.ODCIVARCHAR2LIST(SUBSTR(h,1,2),SUBSTR(h,4,2),SUBSTR(h,7,2),SUBSTR(h,10,2),SUBSTR(h,13,2)))
                        )
                ) t
        )
GROUP BY id;

使用道具 举报

回复
论坛徽章:
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
39#
 楼主| 发表于 2010-12-31 14:18 | 只看该作者
#37的代码不知捅到11gR2的哪根筋了, 不仅执行不了, 连分析执行计划也会"死"掉。
在9i下倒是没问题.

使用道具 举报

回复
论坛徽章:
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
40#
 楼主| 发表于 2010-12-31 17:37 | 只看该作者
经过两天的努力, 终于大功告成。

with thand as (select rownum line, 'A' player, p1 hand from euler_poker
union all
select rownum line, 'B' player, p2 hand from euler_poker
)
,trow as (select rownum rn from dual connect by rownum<=5)
,th1 as (select line, player,hand, rn, translate(substr(hand,rn*3-2,1),'TJQKA', 'ABCDE') val,
substr(hand,rn*3-1,1) suit from thand, trow)

,tsort as (select line, player,hand, row_number() over (partition by line,player,hand order by valcnt desc, val desc) rn, val, suit, valcnt
   from (select line, player,hand, val, suit, count(*) over (partition by line,player,hand,val) valcnt
        from th1 order by count(*) over (partition by line,player,hand, val) desc, val desc))
,tvalsort as (select line, player,hand, replace(sys_connect_by_path(val, '/'),'/','') valsort,
          replace(sys_connect_by_path(valcnt, '/'),'/','') valcnt,
          replace(sys_connect_by_path(suit, '/'),'/','') suitsort
          from tsort where level=5 connect by line=prior line and player=prior player and hand=prior hand and rn=prior rn+1)

,tvalsuit as (select line, player,hand, valsort, valcnt,length(translate( 'DSCH', suitsort|| 'DSCH', suitsort)) suitcnt from tvalsort)
,trank as (select line, player,hand,
case when suitcnt = 1 and valcnt='11111' and to_number(substr(valsort,1,1),'X') - to_number(substr(valsort,5,1),'X')=4 then '9'
     when suitcnt = 1 then '6'
     when valcnt='11111' and to_number(substr(valsort,1,1),'X') - to_number(substr(valsort,5,1),'X')=4 then '5'
     when valcnt='44441' then '8'
     when valcnt='33322' then '7'
     when valcnt='33311' then '4'
     when valcnt='22221' then '3'
     when valcnt='22111' then '2'
     else '0'
     end  || valsort rk
from tvalsuit)
select count(*)
from trank t1, trank t2
where t1.line=t2.line and t1.player='A' and t2.player='B' and t1.rk>t2.rk


  COUNT(*)
----------
       376

Executed in 0.516 seconds

使用道具 举报

回复

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

本版积分规则 发表回复

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