楼主: newkid

首届NoCOUG国际SQL挑战赛

[复制链接]
论坛徽章:
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
41#
发表于 2025-5-10 20:57 | 只看该作者
其实就是多项式乘法
face_value就是次数,概率就是系数。
扔2次,就是多项式的平方,3次就是立方...,新的次数就是face_value点数之和,对应的系数就是这个点数之和的概率
用numpy验证

  1. import numpy as np

  2. p1 = np.poly1d([1/4, 0,1/4,1/4,1/12,1/12,0,1/12])

  3. print ("P1 : ", p1)

  4. mul = np.polymul(p1, p1)

  5. print("\n\npoly1D object : ")
  6. print("squre Result : \n", mul)

  7. mul = np.polymul(mul, p1)

  8. print("\n\npoly1D object : ")
  9. print("cube Result : \n", mul)
复制代码

输出

  1. P1 :        7        5        4           3           2
  2. 0.25 x + 0.25 x + 0.25 x + 0.08333 x + 0.08333 x + 0.08333


  3. poly1D object :
  4. square Result :
  5.          14         12         11          10          9          8
  6. 0.0625 x  + 0.125 x  + 0.125 x  + 0.1042 x  + 0.1667 x + 0.1042 x
  7.           7           6           5           4           3           2
  8. + 0.125 x + 0.04861 x + 0.05556 x + 0.04861 x + 0.01389 x + 0.01389 x + 0.006944


  9. poly1D object :
  10. cube Result :
  11.           21           19           18          17          16
  12. 0.01562 x  + 0.04688 x  + 0.04688 x  + 0.0625 x  + 0.1094 x
  13.             15         14           13          12           11
  14. + 0.09375 x  + 0.125 x  + 0.09896 x  + 0.1042 x  + 0.08854 x
  15.             10           9           8           7           6
  16. + 0.05729 x  + 0.05787 x + 0.03299 x + 0.02778 x + 0.01273 x
  17.              5            4            3            2
  18. + 0.008681 x + 0.006944 x + 0.001736 x + 0.001736 x + 0.0005787
复制代码

下面是sql计算的

  1. SELECT face_value, SUM (probability) AS probability
  2. FROM
  3. (
  4. SELECT
  5. d1.face_value + d2.face_value AS face_value,
  6. d1.probability * d2.probability AS probability
  7. FROM die d1 CROSS JOIN die d2
  8. )
  9. GROUP BY face_value
  10. ┌────────────┬──────────────────────┐
  11. │ face_value │     probability      │
  12. │   int32    │        double        │
  13. ├────────────┼──────────────────────┤
  14. │          2 │               0.0625 │
  15. │          4 │                0.125 │
  16. │          5 │                0.125 │
  17. │          6 │   0.1041666679084301 │
  18. │          7 │   0.1666666679084301 │
  19. │          8 │   0.1041666679084301 │
  20. │          9 │   0.1250000037252903 │
  21. │         10 │ 0.048611112870275974 │
  22. │         11 │  0.05555555783212185 │
  23. │         12 │ 0.048611112870275974 │
  24. │         13 │  0.01388888992369175 │
  25. │         14 │  0.01388888992369175 │
  26. │         16 │ 0.006944444961845875 │
  27. ├────────────┴──────────────────────┤
  28. │ 13 rows                 2 columns │
  29. └───────────────────────────────────┘
复制代码

使用道具 举报

回复
论坛徽章:
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
42#
发表于 2025-5-10 21:02 | 只看该作者
本帖最后由 〇〇 于 2025-5-10 21:11 编辑

因为numpy多项式是x从7次到0次高到低排列,比原式1-8少1,所以平方算出来的值比sql少2,从14次到0次,用16-次数就是sql的面值,原式系数刚好是左右对称,所以从前往后看对应系数就可以了。

既然是多项式乘法,用FFT就容易想到了,https://zhuanlan.zhihu.com/p/76622485

使用道具 举报

回复
论坛徽章:
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
43#
发表于 2025-5-10 21:39 | 只看该作者
本帖最后由 〇〇 于 2025-5-10 21:44 编辑

新版的numpy有多项式乘方的函数,直接用,注意x次数是从右向左的看的,不管算扔300次还是3000次都很快

  1. from numpy.polynomial import polynomial as P

  2. d=[1/12, 0,1/12,1/12,1/4,1/4,0,1/4]



  3. >>> P.polypow(d, 2)
  4. array([0.00694444, 0.        , 0.01388889, 0.01388889, 0.04861111,
  5.        0.05555556, 0.04861111, 0.125     , 0.10416667, 0.16666667,
  6.        0.10416667, 0.125     , 0.125     , 0.        , 0.0625    ])
  7. >>> P.polypow(d, 3)
  8. array([0.0005787 , 0.        , 0.00173611, 0.00173611, 0.00694444,
  9.        0.00868056, 0.01273148, 0.02777778, 0.03298611, 0.05787037,
  10.        0.05729167, 0.08854167, 0.10416667, 0.09895833, 0.125     ,
  11.        0.09375   , 0.109375  , 0.0625    , 0.046875  , 0.046875  ,
  12.        0.        , 0.015625  ])

  13. >>> import time
  14. >>> t=time.time();r=P.polypow(d, 300);print(len(r),time.time()-t)
  15. 2** 0.001355886459350586
  16. >>> t=time.time();r=P.polypow(d, 3000);print(len(r),time.time()-t)
  17. 21001 0.038218021392822266

复制代码

使用道具 举报

回复
论坛徽章:
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
44#
发表于 2025-5-12 10:57 | 只看该作者
不用FFT,用快速幂算法也能提高一些效率,这就是原帖中出题者的解法,矩阵相乘,7次=4次+2次+1次,把连接次数按二进制位分解。

以下是Qwen 3按照我的提示给出的结果

  1. import numpy as np

  2. def poly_pow(poly, power):
  3.     """
  4.     使用快速幂算法计算多项式的N次幂。
  5.    
  6.     参数:
  7.         poly (array-like): 输入多项式,按升幂排列的系数列表或数组。
  8.         power (int): 非负整数,要计算的幂次。
  9.         
  10.     返回:
  11.         result (np.ndarray): 结果多项式系数数组。
  12.     """
  13.     result = np.array([1])  # 初始化为常数项 1(即多项式 1)
  14.     current = np.array(poly, dtype=float)
  15.    
  16.     while power > 0:
  17.         if power % 2 == 1:
  18.             result = np.polymul(result, current)  # result *= current
  19.         current = np.polymul(current, current)   # current ^= 2
  20.         power //= 2
  21.         
  22.     return result

  23. # 示例用法:
  24. if __name__ == "__main__":
  25.     poly = [1, 2]  # 表示 1 + 2x
  26.     n = 5
  27.     res = poly_pow(poly, n)
  28.     print("结果多项式系数:", res)
复制代码

我测试了poly_pow,和polynomial.polypow速度差不多

使用道具 举报

回复
论坛徽章:
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
45#
发表于 2025-5-12 15:30 | 只看该作者
无意中发现,newkid在《剑破冰山》一书中期待的写法在duckdb v1.1.3中已经实现了,这里用3E0代替了绑定变量:N

  1. WITH recursive t(v,p,lvl) AS (
  2.      SELECT face_value  v
  3.            ,probability p
  4.            ,1
  5.        FROM die
  6.      UNION ALL
  7.      SELECT d.face_value + t.v       ---- 第N次投掷,面值为前N-1次的各种结果加上die表的六种可能面值
  8.            ,SUM(d.probability * t.p) ---- 把相同的总面值的概率加起来
  9.            ,MAX(t.lvl)+1            
  10.        FROM die d,t
  11.       WHERE t.lvl<3E0
  12.       GROUP BY d.face_value + t.v    ---- 按照总面值分组聚合,再做下一次递归
  13.       )
  14.   SELECT *
  15.     FROM t
  16.    WHERE lvl = 3E0;
  17. ┌───────┬───────────────────────┬───────┐
  18. │   v   │           p           │  lvl  │
  19. │ int32 │        double         │ int32 │
  20. ├───────┼───────────────────────┼───────┤
  21. │    10 │   0.12499999999999999 │     3 │
  22. │    18 │  0.012731481481481479 │     3 │
  23. │    15 │  0.057870370370370364 │     3 │
  24. │    11 │   0.09895833333333333 │     3 │
  25. │    16 │  0.032986111111111105 │     3 │
  26. │    22 │  0.001736111111111111 │     3 │
  27. │     5 │              0.046875 │     3 │
  28. │    17 │  0.027777777777777776 │     3 │
  29. │     9 │               0.09375 │     3 │
  30. │    14 │   0.05729166666666666 │     3 │
  31. │    24 │ 0.0005787037037037037 │     3 │
  32. │     7 │                0.0625 │     3 │
  33. │    13 │   0.08854166666666667 │     3 │
  34. │     8 │   0.10937499999999999 │     3 │
  35. │    19 │  0.008680555555555556 │     3 │
  36. │    12 │   0.10416666666666667 │     3 │
  37. │     6 │              0.046875 │     3 │
  38. │     3 │              0.015625 │     3 │
  39. │    20 │  0.006944444444444443 │     3 │
  40. │    21 │  0.001736111111111111 │     3 │
  41. ├───────┴───────────────────────┴───────┤
  42. │ 20 rows                     3 columns │
  43. └───────────────────────────────────────┘
复制代码

使用道具 举报

回复
论坛徽章:
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
46#
发表于 2025-5-12 19:56 | 只看该作者
用快速幂法写了个双重递归,N=100和newkid的单次递归也差不多快,更多就精度不足了,全显示0

  1. with recursive
  2.    t(v,p,lvl)AS materialized (
  3.      SELECT face_value  v
  4.            ,probability p
  5.            ,1
  6.        FROM die
  7.      UNION ALL
  8.      SELECT d.v + t.v       ---- 第N次投掷,面值为前N-1次的各种结果加上die表的六种可能面值
  9.            ,SUM(d.p * t.p) ---- 把相同的总面值的概率加起来
  10.            ,MAX(t.lvl)*2            
  11.        FROM t d,t
  12.       WHERE t.lvl<100
  13.       GROUP BY d.v + t.v    ---- 按照总面值分组聚合,再做下一次递归
  14. ),t1 as materialized (select *,dense_rank()over(order by lvl)rk from t where lvl & 100<>0)
  15. , t2(v, p, rk) as materialized (select 0 v, 1::double p, 0 rk
  16. union all
  17. select t1.v + t2.v, sum(t1.p * t2.p),max(t1.rk)from t1,t2 where t1.rk=t2.rk+1 GROUP BY t1.v + t2.v)
  18. select v,p from t2 where rk=(select max(rk) from t2)
  19. order by 1;

  20. │   797 │  1.207467347241364e-106 │
  21. │   798 │  1.207467347241364e-106 │
  22. │   800 │  1.207467347241364e-108 │
  23. ├───────┴─────────────────────────┤
  24. │ 699 rows (40 shown)   2 columns │
  25. └─────────────────────────────────┘
  26. Run Time (s): real 0.050 user 0.046875 sys 0.000000

  27. WITH recursive t(v,p,lvl) AS (
  28.    SELECT face_value  v
  29.          ,probability p
  30.          ,1
  31.      FROM die
  32.    UNION ALL
  33.    SELECT d.face_value + t.v       ---- 第N次投掷,面值为前N-1次的各种结果加上die表的六种可能面值
  34.          ,SUM(d.probability * t.p) ---- 把相同的总面值的概率加起来
  35.          ,MAX(t.lvl)+1            
  36.      FROM die d,t
  37.     WHERE t.lvl<100
  38.     GROUP BY d.face_value + t.v    ---- 按照总面值分组聚合,再做下一次递归
  39.     )
  40. SELECT *
  41.   FROM t
  42. WHERE lvl = 100
  43. order by 1;

  44. │   797 │ 1.2074673472413608e-106 │   100 │
  45. │   798 │ 1.2074673472413608e-106 │   100 │
  46. │   800 │ 1.2074673472413604e-108 │   100 │
  47. ├───────┴─────────────────────────┴───────┤
  48. │ 699 rows (40 shown)           3 columns │
  49. └─────────────────────────────────────────┘
  50. Run Time (s): real 0.103 user 0.203125 sys 0.000000
复制代码

使用道具 举报

回复
论坛徽章:
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
47#
 楼主| 发表于 2025-5-12 23:53 | 只看该作者
你还在玩这个?我记得用11g的递归SQL就可以高效解决了。

使用道具 举报

回复
论坛徽章:
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#
 楼主| 发表于 2025-5-12 23:59 | 只看该作者
刚刚才看到你有几个帖子被审核。
Oracle的递归SQL不能用聚合,但是允许用分析函数,所以可以变通实现。

使用道具 举报

回复
论坛徽章:
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
49#
发表于 2025-5-13 06:10 | 只看该作者
newkid 发表于 2025-5-12 23:59
刚刚才看到你有几个帖子被审核。Oracle的递归SQL不能用聚合,但是允许用分析函数,所以可以变通实现。

对,我记得后来挖雷还是什么题目中你用了。
写书的时候还不行

  1. 回到文档看看递归WITH的限制。文档中说,递归成员不可以包含以下元素:
  2. 1. DISTINCT 关键字或 GROUP BY 子句
  3. 2. model 子句
  4. 3. 聚合函数。而分析函数允许在SELECT中使用。
  5. 4. 引用了当前query_name的子查询
  6. 5. 把当前query_name作为右边的外连接。

  7. 1和3其实是一回事。这个限制使得我们无法在递归过程中通过聚合减少中间结果。限制3说可以用分析函数,但实际的试验结果会报错:
  8. WITH t(n) AS (
  9.   SELECT 1 FROM DUAL
  10.   UNION ALL
  11.   SELECT 1+MAX(n) OVER() FROM t
  12.    WHERE n<3
  13. )
  14. SELECT * FROM t;

  15. SELECT * FROM t
  16.               *
  17. ERROR at line 7:
  18. ORA-32486: unsupported operation in recursive branch of recursive WITH clause

  19. 限制4造成的后果是:如果我已经找到了一个答案,无法告诉ORACLE停止遍历其他的枝条。
  20. 我期望的写法是:
  21. WITH t(...) AS (
  22.    SELECT ...
  23.    UNION ALL
  24.    SELECT ...
  25.      FROM t,...
  26.     WHERE ... AND NOT EXISTS (SELECT 1 FROM t WHERE 找到答案的条件) --- 这个子查询不允许出现t

  27. 如果哪一天这些限制能够去除,这个递归WITH功能将会好玩很多。

  28. 限制5说不可以用于外连接,但是我试了一下并不报错:
复制代码

使用道具 举报

回复
论坛徽章:
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
50#
发表于 2025-5-13 09:25 | 只看该作者
oracle 19c 递归with 还是那么多限制,
https://docs.oracle.com/en/datab ... 6142A51C6__I2077142
连mysql有的方面都比他限制少
https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/with.html

oracle 19c用分析函数,N=30还可以,300就算不动了,可能要加提示

  1. SQL> WITH  t(v,p,lvl,rn) AS (
  2.      SELECT face_value  v
  3.            ,probability p
  4.            ,1
  5. ,1
  6.        FROM die
  7.      UNION ALL
  8.      SELECT d.face_value + t.v       ---- 第N次投掷,面值为前N-1次的各种结果加上die表的六种可能面值
  9.            ,SUM(d.probability * t.p)over(partition BY d.face_value + t.v) ---- 把相同的总面值的概率加起来
  10.            ,(t.lvl)+1 ,row_number()over(partition BY d.face_value + t.v order by 1)            
  11.        FROM die d,t
  12.       WHERE t.lvl<30 and t.rn=1
  13.           ---- 按照总面值分组聚合,再做下一次递归
  14.       )
  15. select count(*) from(
  16.   SELECT v,sum(p)p
  17.     FROM t
  18.    WHERE lvl = 30 and rn=1
  19. group by v)
  20. ;  2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20  

  21.   COUNT(*)
  22. ----------
  23.        209

  24. Elapsed: 00:00:00.49

  25. ------N=300
  26. ERROR at line 20:
  27. ORA-0**3: user requested cancel of current operation


  28. Elapsed: 00:00:22.09
复制代码

使用道具 举报

回复

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

本版积分规则 发表回复

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