參賽選手:張澤鵬
個(gè)人簡(jiǎn)介:杭州隱函科技有限公司聯(lián)創(chuàng),技術(shù)負(fù)責(zé)人
參賽數(shù)據(jù)庫(kù):PostgreSQL
性能評(píng)測(cè):百萬級(jí)數(shù)據(jù)代碼性能評(píng)測(cè) 2.46秒
綜合得分:82.2
以下是張澤鵬選手的代碼說明思路簡(jiǎn)介:
本算法用了取巧的方法:提前計(jì)算好4個(gè)1~10數(shù)值求24的結(jié)果,執(zhí)行查詢時(shí),直接通過特征向量來查詢;思路類似于“相似圖片搜索”,先提前計(jì)算好圖片庫(kù)中每張圖片的特征向量,后續(xù)通過特征向量做相似搜索即可。
算法原理
1. 預(yù)計(jì)算:因?yàn)閌result`中數(shù)值的順序無關(guān),因此先對(duì)`10 ^ 4 = 10000`個(gè)數(shù)組做無序去重,獲得715個(gè)順序無關(guān)的數(shù)組;經(jīng)過計(jì)算可得715中只有566個(gè)組合能計(jì)算出`24`。
2. 將這566個(gè)公式預(yù)置在SQL文件中,即代碼中的`expressions(result)`。
3. 提取公式的特征向量
4. 得知最終測(cè)試數(shù)據(jù)集內(nèi)會(huì)包含重復(fù)的題目(即c1、c2、c3、c4)會(huì)有相同,這些相同的題目只計(jì)算一次。即代碼中`rounds(ids, c1, c2, c3, c4)`。
5. 用相同的方法,計(jì)算`rounds`中每道題目的特征向量,即代碼中的`rounds_features(ids, features)`。
6. 然后根據(jù)特征向量,`rounds_features`從`results`中匹配出結(jié)果,并展開得到每道題目的結(jié)果`cards_results(id, result)`。
7. 最后與原始表`left join`獲得最終結(jié)果。
以下是張澤鵬選手的算法說明,結(jié)尾附完整SQL:
算法說明
1. 文檔概述
本文是 NineData 主辦的數(shù)據(jù)庫(kù)編程大賽頁(yè)面「用一條 SQL 給出撲克牌 24 點(diǎn)的計(jì)算表達(dá)式」的解題思路。我本地測(cè)試的數(shù)據(jù)庫(kù)為 PostgreSQL 16.1。
2. 比賽規(guī)則
根據(jù)題目描述可知:有一張表 cards,包括自增的數(shù)字主鍵字段 id,以及另外 4 個(gè)字段 c1、c2、c3、c4,且這四個(gè)字段的取值范圍是1 → 10之間任意整數(shù)。要求使用一條 SQL 給出這四個(gè)字段計(jì)算 24 的公式。示例如 Table 1 所示:
Table 1: 測(cè)試數(shù)據(jù)示例
其中:
? result 字段是計(jì)算的表達(dá)式,只需返回 1 個(gè)解,如果沒有解,result 返回 null。
? 24 點(diǎn)的計(jì)算規(guī)則:只能使用加減乘除四則運(yùn)算,不能使用階乘、指數(shù)等運(yùn)算符,每個(gè)數(shù)字
最少使用一次,且只能使用一次,可以使用小括號(hào)改變優(yōu)先級(jí)。
? 只能使用一條 SQL,可以使用數(shù)據(jù)庫(kù)內(nèi)置函數(shù),但是不能使用存儲(chǔ)過程或自定義函數(shù)和代碼塊。
根據(jù)上述題目要求,首先在本地創(chuàng)建一張測(cè)試表。代碼如下:
create schema if not exists poker24;
create table if not exists poker24.cards (
?id serial primary key,
?c1 int not null,
?c2 int not null,
?c3 int not null,
?c4 int not null
);
僅根據(jù)題目描述,我原以為本次比賽的測(cè)試數(shù)據(jù)最多就104 = 10000行,但在比賽的答疑群里得知,初賽的測(cè)試數(shù)據(jù)量會(huì)超過 1 萬行(即有重復(fù)的數(shù)據(jù)),而決賽的測(cè)試數(shù)據(jù)量甚至?xí)^100 萬行。因此,生成測(cè)試數(shù)據(jù)時(shí)也需要生成一些重復(fù)的數(shù)據(jù)。以下代碼共生成 128 萬行測(cè)試數(shù)據(jù):
create schema if not exists poker24;
insert into poker24.cards (
?c1, c2, c3, c4
)
select
?t1.c as c1,
?t2.c as c2,
?t3.c as c3,
?t4.c as c4
from
?generate_series(1, 10) as t1(c),
?generate_series(1, 10) as t2(c),
?generate_series(1, 10) as t3(c),
?generate_series(1, 10) as t4(c); -- 1W
insert into poker24.cards (c1, c2, c3, c4) select c1, c2, c3, c4 from poker24.cards;
-- 2W
insert into poker24.cards (c1, c2, c3, c4) select c1, c2, c3, c4 from poker24.cards;
-- 4W
insert into poker24.cards (c1, c2, c3, c4) select c1, c2, c3, c4 from poker24.cards;
-- 8W
insert into poker24.cards (c1, c2, c3, c4) select c1, c2, c3, c4 from poker24.cards;
-- 16W
insert into poker24.cards (c1, c2, c3, c4) select c1, c2, c3, c4 from poker24.cards;
-- 32W
insert into poker24.cards (c1, c2, c3, c4) select c1, c2, c3, c4 from poker24.cards;
-- 64W
insert into poker24.cards (c1, c2, c3, c4) select c1, c2, c3, c4 from poker24.cards;
-- 128W
3. 算法設(shè)計(jì)
3.1. 去除重復(fù)數(shù)據(jù)
從前文的已知條件可知,公式中數(shù)值的順序并不固定。例如 Table 1 中“第 2 行”,數(shù)值順序是“7”、“8”、“9”、“10”;但公式中數(shù)值出現(xiàn)的順序是“8”、“9”、“10”、“7”。同時(shí),
前文也明確提到測(cè)試數(shù)據(jù)中會(huì)有重復(fù)的記錄,如果每行數(shù)據(jù)都要計(jì)算,會(huì)增加許多冗余的計(jì)算。因此,可以按排序后的數(shù)據(jù)——sorted([c1, c2, c3, c4])——做分組,每組只計(jì)算一次,可以避免重復(fù)的計(jì)算。代碼如下:
with numbers(id, number) as ( -- 行轉(zhuǎn)列
?select id, c1 as number from poker24.cards
?union all
?select id, c2 from poker24.cards
?union all
?select id, c3 from poker24.cards
?union all
?select id, c4 from poker24.cards
), rounds as (
?select
?id,
?array_agg(number order by number) as numbers -- 聚合成數(shù)組時(shí)做排序
?from
?numbers
?group by
?id
), rounds_numbers as (
?select
?array_agg(id) as ids, -- 聚合有相同數(shù)值的記錄
?numbers
?from
?rounds
?group by
?numbers
)
執(zhí)行 select count(*) from rounds_numbers 的結(jié)果如下:
?count
-------
?715
(1 行記錄)
即分組后記錄數(shù)量從 128 萬行下降到 715 行。
3.2. 運(yùn)算數(shù)(operand)排列
首先通過排列算法生成4! = 24種運(yùn)算數(shù)的組合。即:
Table 2: 4 個(gè)數(shù)值的排列
因?yàn)榻M合數(shù)量比較少,可以直接手工羅列所有的排列。代碼如下:
-- 接前文代碼
, permutations as (
?select * from (values
?(ARRAY[1, 2, 3, 4]), (ARRAY[1, 2, 4, 3]), (ARRAY[1, 3, 2, 4]),
?(ARRAY[1, 3, 4, 2]), (ARRAY[1, 4, 2, 3]), (ARRAY[1, 4, 3, 2]),
?(ARRAY[2, 1, 3, 4]), (ARRAY[2, 1, 4, 3]), (ARRAY[2, 3, 1, 4]),
?(ARRAY[2, 3, 4, 1]), (ARRAY[2, 4, 1, 3]), (ARRAY[2, 4, 3, 1]),
?(ARRAY[3, 1, 2, 4]), (ARRAY[3, 1, 4, 2]), (ARRAY[3, 2, 1, 4]),
?(ARRAY[3, 2, 4, 1]), (ARRAY[3, 4, 1, 2]), (ARRAY[3, 4, 2, 1]),
?(ARRAY[4, 1, 2, 3]), (ARRAY[4, 1, 3, 2]), (ARRAY[4, 2, 1, 3]),
?(ARRAY[4, 2, 3, 1]), (ARRAY[4, 3, 1, 2]), (ARRAY[4, 3, 2, 1])
?) as t(indices)
), operands as (
?select
?distinct
?ids,
?ARRAY[
?numbers[indices[1]],
?numbers[indices[2]],
?numbers[indices[3]],
?numbers[indices[4]]
?] as numbers
?from
?rounds_numbers,
?permutations
)
3.3. 運(yùn)算符(operator)組合
有了 4 個(gè)運(yùn)算數(shù),需要再填充中間 3 個(gè)運(yùn)算符(operator),每個(gè)運(yùn)算符只能使用四則運(yùn)算(加減乘除),即43 = 64種組合。生成運(yùn)算符的代碼如下:
-- 接前文代碼
, operators as (
?select
?ARRAY[o1.sign, o2.sign, o3.sign] as signs
?from
?unnest(ARRAY['+', '-', '*', '/']) as o1(sign),
?unnest(ARRAY['+', '-', '*', '/']) as o2(sign),
?unnest(ARRAY['+', '-', '*', '/']) as o3(sign)
)
3.4. 優(yōu)先級(jí)(priority)枚舉
通過加小括號(hào)改變運(yùn)算符優(yōu)先級(jí),可以產(chǎn)生以下 5 種計(jì)算順序:
1. ((?? ° ??) ° ??) ° ??
2. (?? ° (?? ° ??)) ° ??
3. (?? ° ??) ° (?? ° ??)
4. ?? ° ((?? ° ??) ° ??)
5. ?? ° (?? ° (?? ° ??))
直接在代碼中描述括號(hào)的位置比較困難,所以我用一個(gè)長(zhǎng)度為 3 的數(shù)組 priorities 記錄第 i
次運(yùn)算時(shí),計(jì)算第 priorities[i]個(gè)運(yùn)算符。例如表達(dá)式(?? ° ??) ° (?? ° ??):
? 第 1 次計(jì)算,計(jì)算?? ° ??,得到結(jié)果??1;其中°是當(dāng)前表達(dá)式 (?? ° ??) ° (?? ° ??) 中是第 1 個(gè)運(yùn)算符,因此數(shù)組 priorities[1] = 1。
? 第 2 次計(jì)算,計(jì)算?? ° ??,得到結(jié)果??2;其中°是當(dāng)前表達(dá)式 ??1
° (?? ° ??) 中是第 2 個(gè)運(yùn)算符,因此數(shù)組 priorities[2] = 2。
? 第 3 次計(jì)算,計(jì)算??1
° ??2,得到結(jié)果??3;其中°是當(dāng)前表達(dá)式 ??1
° ??2 中是第 1 個(gè)運(yùn)算符,因此數(shù)組 priorities[3] = 1。
綜上所述,表達(dá)式 (?? ° ??) ° (?? ° ??) 的優(yōu)先級(jí)數(shù)組是 ARRAY[1, 2, 1]。5 種優(yōu)先級(jí)的數(shù)組表達(dá)式如下:
? ((?? ° ??) ° ??) ° ??:ARRAY[1, 1, 1]。
? (?? ° (?? ° ??)) ° ??:ARRAY[2, 1, 1]。
? (?? ° ??) ° (?? ° ??):ARRAY[1, 2, 1]。
? ?? ° ((?? ° ??) ° ??):ARRAY[2, 2, 1]。
? ?? ° (?? ° (?? ° ??)):ARRAY[3, 2, 1]。
對(duì)應(yīng)的代碼如下:
-- 接前文代碼
, priorities(orders) as (
?select ARRAY[1, 1, 1]
?union all
?select ARRAY[2, 1, 1]
?union all
?select ARRAY[1, 2, 1]
?union all
?select ARRAY[2, 2, 1]
?union all
?select ARRAY[3, 2, 1]
)
3.5. 計(jì)算表達(dá)式
運(yùn)算數(shù)、運(yùn)算符,以及運(yùn)算順序都準(zhǔn)備就緒后,如前文描述,接著按照 priorities 的順序逐一執(zhí)行運(yùn)算。迭代邏輯如下:
?迭代初始化:
? 需要先整數(shù)類型的數(shù)值數(shù)組 numbers float[]轉(zhuǎn)換成浮點(diǎn)數(shù)類型,避免除法運(yùn)算丟失精度。
? 同時(shí)生成數(shù)值數(shù)組對(duì)應(yīng)的字符串?dāng)?shù)組 terms text[],用于后續(xù)生成表達(dá)式字符串。
? 迭代 3 輪,第 i 輪迭代時(shí):
? 彈出運(yùn)算符 operator:signs[orders[i]]。
? 彈出第一個(gè)運(yùn)算數(shù) operand1:numbers[orders[i]]。
? 彈出第二個(gè)運(yùn)算數(shù) operand2:numbers[orders[i] + 1]。
? 若 operator 是除法且 operand2 為零,則返回 null。
? 否則,計(jì)算結(jié)果并寫回到運(yùn)算數(shù) numbers[orders[i]]。
? 彈出第一個(gè)表達(dá)式 expression1:terms[orders[i]]。
? 彈出第二個(gè)表達(dá)式 expression2:terms[orders[i] + 1]。
? 將表達(dá)式'(' || expression1 || operator || expression2 || ')'寫回到terms[orders[i]]。
? 繼續(xù)下一輪迭代,直到結(jié)束。
雖然 SQL 語法未提供 for、while 等循環(huán)語句,但可以使用遞歸的 CTE 模擬循環(huán)。代碼如下:
-- 接前文代碼
, expressions (ids, level, numbers, signs, orders, terms) as (
?select
?ids,
?4 as level,
?numbers::float[] as numbers,
?signs,
?orders,
?numbers::text[] as terms
?from
?operands,
?operators,
?priorities
?union all
?select
?ids,
?level - 1 as level,
?case
?when signs[orders[1]] = '/' and numbers[orders[1] + 1] = 0 -- 除 0 情況特殊判斷
?then null
?when signs[orders[1]] = '+' -- 加法運(yùn)算
?then numbers[:orders[1] - 1]
?|| ARRAY[numbers[orders[1]] + numbers[orders[1] + 1]]
?|| numbers[orders[1] + 2:]
?when signs[orders[1]] = '-' -- 減法運(yùn)算
?then numbers[:orders[1] - 1]
?|| ARRAY[numbers[orders[1]] - numbers[orders[1] + 1]]
?|| numbers[orders[1] + 2:]
?when signs[orders[1]] = '*' -- 乘法運(yùn)算
?then numbers[:orders[1] - 1]
?|| ARRAY[numbers[orders[1]] * numbers[orders[1] + 1]]
?|| numbers[orders[1] + 2:]
?else
?numbers[:orders[1] - 1] -- 除法運(yùn)算
?|| ARRAY[numbers[orders[1]] / numbers[orders[1] + 1]]
?|| numbers[orders[1] + 2:]
?end as numbers,
?signs[:orders[1] - 1] || signs[orders[1] + 1:] as signs, -- 移除運(yùn)算符
?orders[2:], -- 移除運(yùn)算順序
?terms[:orders[1] - 1]
?|| ARRAY['('
?|| terms[orders[1]]
?|| signs[orders[1]]
?|| terms[orders[1] + 1]
?|| ')']
?|| terms[orders[1] + 2:]
?as terms -- 合成表達(dá)式
?from
?expressions
?where
?level > 1
?and numbers is not null
)
3.6. 篩選結(jié)果
然后從上述計(jì)算結(jié)果中篩選出結(jié)果值是 24 的表達(dá)式。因?yàn)轭}目要求只展示一個(gè)結(jié)果,在
PostgreSQL 中可以使用 distinct on 快速過濾出第一個(gè)結(jié)果,完整代碼如下:
-- 接前文代碼
, results (ids, result) as (
?select
?distinct on (ids)
?ids,
?terms[1] as result
?from
?expressions
?where
?level = 1
?and numbers is not null
?and round(numbers[1] * 1000000.0) = 24000000
)
4. 算法優(yōu)化
在前文代碼的基礎(chǔ)上執(zhí)行 select * from results 就能算出 24 的表達(dá)式,但計(jì)算過程非常耗
時(shí)。在我的電腦上,10000 行記錄需要執(zhí)行了 20 多秒,最終能得到 566 個(gè)表達(dá)式,如
Table 3 所示:
Table 3: 10000 行數(shù)據(jù)計(jì)算結(jié)果
4.1. 結(jié)果預(yù)置
經(jīng)過驗(yàn)證,這 566 個(gè)表達(dá)式都正確,但性能非常糟糕,于是我想:如果直接把 566 個(gè)結(jié)果硬編碼到代碼里,性能是不是就能得到提升。嘗試把這 566 個(gè)表達(dá)式輸出到文件 results.txt中,用 du -bs results.txt 命令可知總共 8KB,符合題目中代碼文件小于 10KB 的要求!通過移除多于的空格,例如((1 + (2 + 1)) × 6)可以精簡(jiǎn)成(1 + 2 + 1) × 6,最終文件尺寸縮減到5KB,這樣就足夠?qū)懯S嗟拇a了:
with results(result) as (values
?('8*10-7*8'),
?('8-8*(7-9)'),
?('8*9/(10-7)'),
?('8+(10-8)*8'),
?...
),
4.2. 字符串型特征值
現(xiàn)在問題變成了如何把結(jié)果與 poker24.cards 里的記錄匹配上。我想到了 PostgreSQL 有一個(gè)以圖搜圖的插件 imgsmlr,它會(huì)預(yù)先計(jì)算圖片的特征向量,然后通過匹配特征向量來代替圖片的搜索。這個(gè)技巧在落地算法模型時(shí)很常用,甚至還有專門為這個(gè)場(chǎng)景定制的向量數(shù)據(jù)庫(kù)。
借鑒這個(gè)思路,可以對(duì)結(jié)果集和數(shù)據(jù)集做預(yù)處理,求出各自的特征向量,然后用特征向量了匹配即可。
根據(jù)前文的描述,很容易想到一個(gè)表達(dá)式的特征就是其中的 4 個(gè)運(yùn)算數(shù)按順序排列。例如((1 + (2 + 1)) × 6)的特征值是 ARRAY[1, 1, 2, 6]。為了方便后續(xù) left join,需要選擇一種方便做等值運(yùn)算的數(shù)據(jù)類型保存這個(gè)特征值。我首先想到的是把這個(gè)數(shù)組轉(zhuǎn)成用分號(hào)分隔的字符串,即"1;1;2;6"。
在 PostgreSQL 數(shù)據(jù)庫(kù)中,可以先用 regexp_replace 函數(shù)剔除表達(dá)式中的括號(hào),然后再用regexp_split_to_table 函數(shù)將運(yùn)算符作為分隔符把表達(dá)式拆分成4 個(gè)子字符串,最后通過string_agg 窗口函數(shù)將子字符串排序后合并成特征值。代碼如下:
-- 接前文代碼
?
, results_terms as (
?select
?result,
?regexp_split_to_table(
?regexp_replace(result, '[()]+', '', 'g'),
?'[-+*/]'
?) as term
?from
?results
), results_features as (
?select
?result,
?string_agg(term, ';' order by term::int) as feature
?from
?results_terms
?group by
?result
?order by
?feature
)
poker24.cards 中的數(shù)據(jù)也采用相同的方式做預(yù)處理,接著將兩個(gè)結(jié)果集做 left join 得到最終結(jié)果。
4.3. 整型特征值
字符串型特征值在我本地執(zhí)行,128 萬行測(cè)試數(shù)據(jù)耗時(shí) 1 秒出頭,所以我就沒繼續(xù)優(yōu)化直接提交了。但在決賽服務(wù)器上評(píng)測(cè)結(jié)果是 2 秒多。為了進(jìn)一步縮短時(shí)間,可以用整數(shù)類型代替字符串類型,畢竟整數(shù)的處理速度優(yōu)于字符串。
在機(jī)器學(xué)習(xí)算法中,有一種獨(dú)熱編碼的數(shù)據(jù)預(yù)處理技巧,用來預(yù)處理一些無序的枚舉值。例如將性別的“男”和“女”分別編碼成“01”與“10”。本題的運(yùn)算數(shù)也是無序的,所以也可以借鑒這個(gè)思路,分別用“0”、“1”、“10”、“100”、……“10000000”、“100000000”代替“1”、“2”、“3”、“4”、……、“9”、“10”,然后將結(jié)果累加起來。例如 ARRAY[1, 1, 2, 6]編碼成0 + 0 + 1 + 10000 = 10001。修改后的代碼如下:
, results_terms as (
?select
?result,
?regexp_split_to_array(
?regexp_replace(result, '[()]+', '', 'g'),
?'[-+*/]'
?)::int[] as terms
?from
?results
), results_features as (
?select
?result,
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])
[terms[1]] +
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])
[terms[2]] +
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])
[terms[3]] +
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])
[terms[4]]
?as feature
?from
?results_terms
?order by
?feature
), cards_features as (
?select
?*,
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])[c1] +
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])[c2] +
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])[c3] +
?(ARRAY[0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000])[c4]
?as feature
?from
?poker24.cards
?order by
?feature
)
select
?cards_features.id,
?cards_features.c1,
?cards_features.c2,
?cards_features.c3,
?cards_features.c4,
?results_features.result
from
?cards_features
left join
?results_features
on
?cards_features.feature = results_features.feature
其中有一個(gè)優(yōu)化小技巧:results_features 與 cards_features 兩張表都先按照求得的 feature排序,最終在 JOIN 的時(shí)候就會(huì)用 merge join 代替 nest loop,執(zhí)行效率更高。經(jīng)過優(yōu)化后,128 萬行測(cè)試數(shù)據(jù)在我電腦上之需耗時(shí) 0.7 秒。
5. 總結(jié)
本文詳細(xì)地解釋了我參與比賽時(shí)的解題過程,但由于能力有限,并且最近事情較多,并沒有做過多的優(yōu)化。希望有更優(yōu)方案的小伙伴不吝賜教!
參賽完整SQL:
with expressions(result) as (values ('8*10-7*8'),('8-8*(7-9)'),('8*9/(10-7)'),('8+(10-8)*8'),
('(1+1+1)*8'),('2*(1+10+1)'),('6*(2+1+1)'),('(1+2)*(1+7)'),('(2+1)*8*1'),('(9-1)*(2+1)'),('3*(10-1-1)'),('(1+1)*4*3'),('(3+1)*(5+1)'),('(3+1)*6*1'),
('(1+7)*1*3'),('8*3*1*1'),('3*1*(9-1)'),('4+(1+1)*10'),('4*(4+1+1)'),('1*(1+5)*4'),('6*1*4*1'),('(7-1)*1*4'),('1*8*(4-1)'),('(1-9)*(1-4)'),
('(5-1)*(5+1)'),('6*1*(5-1)'),('(1+1)*(7+5)'),('(5-1-1)*8'),('6*(6-1-1)'),('8*6/(1+1)'),('6+9*(1+1)'),('10+7*(1+1)'),('8+(1+1)*8'),('2*(2+10*1)'),
('4*2*(1+2)'),('2*2*(5+1)'),('(2+2)*1*6'),('2*2*(7-1)'),('8*(2-1+2)'),('(9+1+2)*2'),('3+2*10+1'),('2*(1+3)*3'),('4*3*2*1'),('(2+5+1)*3'),
('6*2*(3-1)'),('3*(7+2-1)'),('(2-1)*8*3'),('(3+9)*2*1'),('2*10+4*1'),('(2+4)*4*1'),('(2-1+5)*4'),('(6+2)*(4-1)'),('2*(1+4+7)'),('2*(8+4*1)'),
('2*(4-1+9)'),('2*10+5-1'),('5*5+1-2'),('6*(5-2+1)'),('2*1*(7+5)'),('(8+5-1)*2'),('5+1+2*9'),('(2+1)*10-6'),('2*(6+1*6)'),('(6-2)*(7-1)'),
('8*6/2*1'),('1*2*9+6'),('10+2*1*7'),('(7*7-1)/2'),('8*2+1+7'),('9+7*2+1'),('10-(1-8)*2'),('(8/2-1)*8'),('8*9/(1+2)'),('10+3+10+1'),
('3*(10-3+1)'),('(3+1)*(3+3)'),('(3*1+3)*4'),('(1*5+3)*3'),('(6-1+3)*3'),('1*7*3+3'),('3+(8-1)*3'),('(3*9-3)*1'),('4-(1-3)*10'),('4*(3-1+4)'),
('5*4+1+3'),('6/(1-3/4)'),('(1+3)*7-4'),('8/(4/3-1)'),('1-4+9*3'),('3*5+10-1'),('1+5+6*3'),('(3-1)*(5+7)'),('3*5+1+8'),('5*3+1*9'),
('10*3*1-6'),('1*6*3+6'),('1*(7-3)*6'),('6*8/(3-1)'),('6+(3-1)*9'),('1-7+3*10'),('(3-7)*(1-7)'),('8*(7-1-3)'),('(1+7)*9/3'),('8/3*(10-1)'),
('8/3*(8+1)'),('8*9*1/3'),('(10+1)*3-9'),('9*(9-1)/3'),('10+10+4*1'),('4*(10-4)*1'),('4+4*(1+4)'),('1*5*4+4'),('(6-1)*4+4'),('1*7*4-4'),
('4*1*4+8'),('9-1+4*4'),('(10-4)*(5-1)'),('5*4+5-1'),('4/(1-5/6)'),('4*7-5+1'),('(8-4)*(5+1)'),('9+(4-1)*5'),('(4-1)*10-6'),('6-(1-4)*6'),
('6*(7-4+1)'),('(8-4)*6*1'),('6*(9-1-4)'),('(7-4)*(1+7)'),('(7-4)*1*8'),('(4-7)*(1-9)'),('4*8*1-8'),('4*8+1-9'),('10+9+4+1'),('10+5-1+10'),
('(10-5)*5-1'),('5*(5-1/5)'),('6*5-1-5'),('(9-5)*(5+1)'),('(1+5)*(10-6)'),('1*(6*5-6)'),('6*5+1-7'),('6*(1-5+8)'),('(9-5)*1*6'),('7*5-1-10'),
('(1-5+7)*8'),('(7-1)*(9-5)'),('8+10+1+5'),('(5-1)*8-8'),('(5-8)*(1-9)'),('10+5+9*1'),('9+9+5+1'),('(1*10-6)*6'),('(6-1)*6-6'),('6/(1-6/8)'),
('6*(1+9-6)'),('6*(10+1-7)'),('(7+1)*(9-6)'),('8+1*10+6'),('8*(8+1-6)'),('1+8+6+9'),('6+10+9-1'),('1*9+9+6'),('7*1+10+7'),('1+7+9+7'),
('10+7-1+8'),('1+8+8+7'),('8*(1+9-7)'),('(9-1)*(10-7)'),('9-1+9+7'),('(1-8+10)*8'),('8*1+8+8'),('8-1+9+8'),('2+2+10+10'),('10*2+2+2'),
('2*2*2*3'),('4*(2+2+2)'),('2*(2+2*5)'),('(2*7-2)*2'),('2*(2+8+2)'),('2+(9+2)*2'),('(3+10)*2-2'),('2*(3+3)*2'),('3*(2+4+2)'),('3*(5*2-2)'),
('(6-2)*3*2'),('(2+7+3)*2'),('8*3*2/2'),('2*3+2*9'),('(4-2)*(10+2)'),('(2*4+4)*2'),('(2/2+5)*4'),('4*6-2+2'),('7*4-2-2'),('(8/2+2)*4'),
('2+4+2*9'),('(5+2)*2+10'),('(5+2+5)*2'),('2+2*(5+6)'),('2*7+5*2'),('(5+8)*2-2'),('2*(5+9-2)'),('2+10+6*2'),('6*(2+6)/2'),('6+(7+2)*2'),
('(8+6-2)*2'),('(9*2-6)*2'),('2*(10/2+7)'),('2*(7+7-2)'),('2+8+2*7'),('10*2+8/2'),('8*(8-2)/2'),('9*2+8-2'),('10-2*(2-9)'),('(10-3)*2+10'),
('(10/2+3)*3'),('(2+3+3)*3'),('2*(5*3-3)'),('2*(3+6+3)'),('3*(3-2+7)'),('(3+3)*8/2'),('(9-3+2)*3'),('4*3+2+10'),('3*4/2*4'),('2*(3+4+5)'),
('3*(6+4/2)'),('(7-3)*(2+4)'),('(8-4)*2*3'),('2/3*9*4'),('2*(10-3+5)'),('3*(5-2+5)'),('2*5*3-6'),('5+7*3-2'),('5+3+2*8'),('(2-5)+9*3'),
('(10-2)*(6-3)'),('2*6/3*6'),('(6/2)+7*3'),('8+6*3-2'),('3+9+6*2'),('7+10*2-3'),('7+2*7+3'),('2*(7+8-3)'),('9-3*(2-7)'),('10+8+2*3'),
('(8-3-2)*8'),('8*(9-2*3)'),('2+3+9+10'),('2*3+9+9'),('10*(4/10+2)'),('(4-2)*10+4'),('4*(4+4-2)'),('(5*2-4)*4'),('2+6+4*4'),('2*4*(7-4)'),
('(8+4)*4/2'),('(9-2)*4-4'),('10+4+2*5'),('4+(5+5)*2'),('4*5+6-2'),('(7+5)/2*4'),('2*4*(8-5)'),('(2+4)*(9-5)'),('(2-6+10)*4'),('6*(6-4+2)'),
('4+6+2*7'),('6*2/4*8'),('(9-6)*2*4'),('10+7*4/2'),('(7+7)*2-4'),('8/2*7-4'),('7+9+2*4'),('8+2+10+4'),('8-8*(2-4)'),('8*(9-4-2)'),
('9*2-4+10'),('2+4+9+9'),('2*(10/5+10)'),('5*(5-2/10)'),('7*2+5+5'),('(5/5+2)*8'),('5+5*2+9'),('6*2*10/5'),('6*(5*2-6)'),('6*(7-5+2)'),
('2+5*6-8'),('6*5/2+9'),('2+10+7+5'),('7+5*2+7'),('(5*2-7)*8'),('7*5-2-9'),('(10-5-2)*8'),('8*5-8*2'),('2*(9-5+8)'),('9+10*2-5'),
('10+6+10-2'),('2+6+10+6'),('6+6*6/2'),('6*(7-6/2)'),('6*(2+8-6)'),('(6+2)*(9-6)'),('(10-7)*(6+2)'),('8*(2-6+7)'),('7+6+9+2'),('(6-10)*(2-8)'),
('6+8+8+2'),('9*8*2/6'),('(10-2)*(9-6)'),('2*(9+9-6)'),('(10-7)*(10-2)'),('7*(10/7+2)'),('7+2+8+7'),('(8-7+2)*8'),('(9+7)*2-8'),('10-2+7+9'),
('(10-8)*(10+2)'),('8+10-2+8'),('8*(8/8+2)'),('8*(9+2-8)'),('8*(10+2-9)'),('9+9+8-2'),('10/2+10+9'),('3+(10-3)*3'),('3*3*3-3'),('(3+4)*3+3'),
('3*3+3*5'),('6*3+3+3'),('(7-3)*(3+3)'),('3*8*3/3'),('3*(9-3/3)'),('(3*4-4)*3'),('(5+4)*3-3'),('4*(3-3+6)'),('3*(4-3+7)'),('3*8*(4-3)'),
('3+9+3*4'),('10+3*3+5'),('5*5-3/3'),('(6-3)*(3+5)'),('3*(5*3-7)'),('(5+3)/3*9'),('3*(10-6/3)'),('3*(6+6/3)'),('3-(3-6)*7'),('8*(6+3)/3'),
('9-(3-3*6)'),('7*(3+3/7)'),('7+8+3*3'),('(3-7)*(3-9)'),('3+3+8+10'),('8/(3-8/3)'),('9-3*(3-8)'),('3+10*3-9'),('9+3+9+3'),('10*3-10+4'),
('(10-3)*4-4'),('(3+4)*4-4'),('3+5+4*4'),('(6-4)*4*3'),('4*(3-4+7)'),('3*(4+8-4)'),('9*4-4*3'),('3*4/5*10'),('3*5+5+4'),('(5-4+3)*6'),
('(5+7-4)*3'),('(3+5)*4-8'),('(9-5+4)*3'),('10-4+6*3'),('3*4+6+6'),('(4+8)/3*6'),('4*(9-6+3)'),('(7-3)*(10-4)'),('3-7+4*7'),('8-(3-7)*4'),('(7+4)*3-9'),
('(8+10)*4/3'),('4+9+8+3'),('(9+9)*4/3'),('3*(10-10/5)'),('6*(3+5/5)'),('(7+5)*(5-3)'),('8*3*5/5'),('3*(9-5/5)'),('(10/5+6)*3'),('6*(6-5+3)'),
('(5+7)/3*6'),('3*8*(6-5)'),('3*(9-6+5)'),('10-(3-5)*7'),('5*7-8-3'),('3+5+9+7'),('5+8+8+3'),('5-8+3*9'),('9+3*(10-5)'),('9+9*5/3'),
('10*(3-6/10)'),('(6-3)*10-6'),('6*(3+6/6)'),('(6/6+7)*3'),('8*6*3/6'),('3+6+6+9'),('10+6*7/3'),('(7/7+3)*6'),('(8-7+3)*6'),('3*(6+9-7)'),
('(6+10-8)*3'),('8+8/3*6'),('8*9/(6-3)'),('(3-9+10)*6'),('(9-6)*9-3'),('10-3+10+7'),('3*7-7+10'),('7+7+7+3'),('8*(3-7+7)'),('3*(9-7/7)'),
('3*8*(8-7)'),('(7+9-8)*3'),('7-(10-9*3)'),('3*(7+9/9)'),('10*3/10*8'),('(10*8-8)/3'),('8*8*3/8'),('3*(9-8/8)'),('9+8+10-3'),('3*8+9-9'),
('3*(9-10/10)'),('3*(9-10+9)'),('9+9+9-3'),('(10*10-4)/4'),('10*4-4*4'),('4+4*4+4'),('4*(5+4/4)'),('4*4/4*6'),('(7-4)*(4+4)'),('4*8-4-4'),('4-4*(4-9)'),
('4+4*(10-5)'),('4*(5+5-4)'),('4*6*(5-4)'),('(7-5+4)*4'),('(4+4)*(8-5)'),('4+4+6+10'),('(6-4)*(8+4)'),('4*9*4/6'),('(10-7)*(4+4)'),('7*(4-4/7)'),
('4*7-8+4'),('7+4+4+9'),('4*(10+4-8)'),('8+8+4+4'),('9*4-4-8'),('10/5*10+4'),('5+5+4+10'),('5*5-5+4'),('6*4*5/5'),('4*(7-5/5)'),
('8*(4-5/5)'),('5*4-5+9'),('(10-6)+4*5'),('6*(6-5)*4'),('(6-4)*(5+7)'),('8*(4+5-6)'),('4+6+9+5'),('4+10*(7-5)'),('4*(7/7+5)'),('5+8+4+7'),
('9-(4-7)*5'),('(4+8)*10/5'),('4*(8/8+5)'),('(9+5-8)*4'),('4*(5+10-9)'),('4*(9/9+5)'),('6*4*10/10'),('(10+6)/4*6'),('(6-6+6)*4'),('6*4*(7-6)'),
('6+4+8+6'),('(9-4)*6-6'),('4*7+6-10'),('6+4+7+7'),('(6+4-7)*8'),('(7+9)/4*6'),('4+10*(8-6)'),('8*6/8*4'),('6+8/4*9'),('(10*9+6)/4'),
('4*9/9*6'),('4*(7-10/10)'),('4*(7-7/7)'),('8*(4-7/7)'),('10+8*7/4'),('8*7-4*8'),('9*8/(7-4)'),('4-10*(7-9)'),('4*(7-9/9)'),('8+10-4+10'),
('(8+4)*(10-8)'),('8*(4-8/8)'),('(8+4-9)*8'),('8*(9+4-10)'),('8*(4-9/9)'),('9-(4-9-10)'),('5*5-10/10'),('5*5-5/5'),('5-6+5*5'),('5+9+5+5'),
('5*(6-6/5)'),('7*5-5-6'),('5+5+8+6'),('(5+7)/5*10'),('(5+7)*(7-5)'),('8*(5+5-7)'),('(10+5)*8/5'),('5*5-8/8'),('8-9+5*5'),('9+5*5-10'),
('5*5-9/9'),('(10+10)*6/5'),('(6+6)*10/5'),('6*(5-6/6)'),('7+6+5+6'),('6+6*(8-5)'),('9*6-6*5'),('6*(5-7/7)'),('6*(5-8+7)'),('6+(7-5)*9'),
('8*5-6-10'),('8*(6+5-8)'),('6*(8-9+5)'),('6*(9-10+5)'),('(9-6)*5+9'),('10+10*7/5'),('(7-5)*7+10'),('(7+5)*(9-7)'),('(10-8)*(5+7)'),('(7+8)*8/5'),
('5*8-7-9'),('9+5*(10-7)'),('(5-10+8)*8'),('5*8-8-8'),('9*8/(8-5)'),('9+10-5+10'),('10+10+10-6'),('10*6-6*6'),('6+6+6+6'),('(6+6)*(8-6)'),
('6+(9-6)*6'),('6-(7-10)*6'),('6*(7+6-9)'),('6*(8-10+6)'),('6*8/(8-6)'),('6-(6-8)*9'),('(10+6)*9/6'),('(10-7)*10-6'),('6*(7+7-10)'),('10-7*(6-8)'),
('6*8/(9-7)'),('9/6*(9+7)'),('(10-6)*8-8'),('8+(8-6)*8'),('9*8-8*6'),('6-(8-10)*9'),('8*9/(9-6)'),('9+9*10/6'),('10-7*(7-9)'),('(10-8)*7+10')
), vectors as (
select
result,
regexp_split_to_table(regexp_replace(result, '[()]+', '', 'g'), '[-+*/]') as feature
from
expressions
), results as (
select
result,
string_agg(feature, ';' order by feature::int) as features
from
vectors
group by
result
), rounds as (
select
array_agg(id) as ids,
c1, c2, c3, c4
from
poker24.cards
group by
c1, c2, c3, c4
), rounds_cards as (
select ids, c1 as feature from rounds
union all
select ids, c2 as feature from rounds
union all
select ids, c3 as feature from rounds
union all
select ids, c4 as feature from rounds
), rounds_features as (
select
ids,
string_agg(feature::text, ';' order by feature) as features
from
rounds_cards
group by
ids
), rounds_results as (
select
rounds_features.ids,
results.result
from
rounds_features
inner join
results
on
rounds_features.features = results.features
), cards_results as (
select unnest(ids) as id, result from rounds_results
)
select
cards.*,
cards_results.result
from
poker24.cards
left join
cards_results
on
cards.id = cards_results.id
order by
??cards.id;
數(shù)據(jù)庫(kù)編程大賽
下一次再聚!
感謝大家對(duì)本次《數(shù)據(jù)庫(kù)編程大賽》的關(guān)注和支持,歡迎加入技術(shù)交流群,更多精彩活動(dòng)不斷,我們下次再相聚!文章來源:http://www.zghlxwxcb.cn/news/detail-784955.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-784955.html
到了這里,關(guān)于張澤鵬:用PostgreSQL征服24點(diǎn)撲克牌算法的數(shù)據(jù)庫(kù)高手的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!