2017-01-13 10 views
-2

ランダムな整数を生成する問題は、異なるプログラミング言語でポップアップし続け、R,JavaおよびPythonのSOソリューションがあります。SQL:指定された合計にNを加算してN個の非負整数を生成するM

この質問は、オプションの共通テーブル式[CTE]を持つ単一のSELECTステートメントに限定された "バニラ" SQLソリューションを求めています。

入力は、MおよびNintタイプの列を持つ単一の行inputsです。結果は、Mになる整数の場合は、単一の列がiのNx1テーブルになります。

+2

感謝通知するation。しかしここであなたは質問をすることになっています – RiggsFolly

+1

+++これは実際には本当に厳しいSQLの質問です。私は挑戦します! –

+1

@RiggsFollyこの質問は、特定の言語機能を使用するSQLソリューションを求めています。入力と出力を指定します。それは質問に答えるのを助けるために、3つの他の言語のSOソリューションを強調します。あなたの意見では、正確に何が欠けていますか?疑問符? – Sim

答えて

3

私は次のクエリ(PostgreSQLの)提案:

WITH ZeroToOne (m, n, y) AS (
    SELECT m, n, random() 
    FROM inputs 
    CROSS JOIN generate_series(1, n) 
), SumToM (m, n, y, x) AS (
    SELECT m, n, y, y * m/sum(y) OVER (PARTITION BY m, n) 
    FROM zerotoone 
), MissingToM (m, n, l) AS (
    SELECT m, n, m - sum(floor(x)) 
    FROM sumtom 
    GROUP BY m, n 
) 
SELECT m, n, y, x, l, 
    CASE 
    WHEN row_number() OVER (PARTITION BY m, n ORDER BY x - floor(x) DESC) > l 
    THEN floor(x) 
    ELSE ceil(x) 
    END AS v 
FROM missingtom 
NATURAL JOIN sumtom; 

のみ興味深い値をmであり、nおよびV。私は説明のために他の値を残しました。

Iが実行中で、例として以下の入力の場合とクエリをステップう

SELECT * FROM inputs; 
m | n 
----+--- 
20 | 4 
30 | 4 
42 | 3 
(3 rows) 

最初のCTE(ZeroToOne)は、各入力の場合、コールは[1,0]の範囲内nランダムな値を計算しますこれらの値y

m | n |   y   
----+---+--------------------- 
20 | 4 | 0.374425032641739 
20 | 4 | 0.644279096741229 
20 | 4 | 0.626386553514749 
20 | 4 | 0.320786282420158 
30 | 4 | 0.848764919675887 
30 | 4 | 0.268079651053995 
30 | 4 | 0.250213726423681 
30 | 4 | 0.497460773680359 
42 | 3 | 0.571454062592238 
42 | 3 | 0.00338772451505065 
42 | 3 | 0.139226260595024 

第二CTE(SumToM)はmによって各y値を乗算し、入力された場合の値の和で結果を分割します。結果として、入力対(M、N)のためxの全てを合計するmを与える:

m | n |   y   |   x   
----+---+---------------------+------------------- 
20 | 4 | 0.374425032641739 | 3.80924177094873 
20 | 4 | 0.644279096741229 | 6.55462277759638 
20 | 4 | 0.626386553514749 | 6.37259161753762 
20 | 4 | 0.320786282420158 | 3.26354383391728 
30 | 4 | 0.848764919675887 | 13.6565766414436 
30 | 4 | 0.268079651053995 | 4.3133855037604 
30 | 4 | 0.250213726423681 | 4.02592384820881 
30 | 4 | 0.497460773680359 | 8.00411400658722 
42 | 3 | 0.571454062592238 | 33.6117414945302 
42 | 3 | 0.00338772451505065 | 0.199258922297338 
42 | 3 | 0.139226260595024 | 8.18899958317244 

mはx値の整数部分の合計よりも大きいことは明らかです。 2つの合計(x値の和とx値の整数部分の合計)の差がn未満であることもわかります。だから今のアイデアは多くの数字を切り上げなければならず、どれだけ切り捨てなければならないのかということを数えることです。第三のCTE(MissingToM)のL値は切り上げされる値の数である:

m | n | l 
----+---+--- 
20 | 4 | 2 
30 | 4 | 1 
42 | 3 | 1 

数の分布が均一のままであることを確実にするために、我々は、と最高の小数部分を有する数値を切り上げ最後のクエリ:

m | n |   y   |   x   | l | v 
----+---+---------------------+-------------------+---+---- 
20 | 4 | 0.374425032641739 | 3.80924177094873 | 2 | 4 
20 | 4 | 0.644279096741229 | 6.55462277759638 | 2 | 7 
20 | 4 | 0.626386553514749 | 6.37259161753762 | 2 | 6 
20 | 4 | 0.320786282420158 | 3.26354383391728 | 2 | 3 
30 | 4 | 0.848764919675887 | 13.6565766414436 | 1 | 14 
30 | 4 | 0.268079651053995 | 4.3133855037604 | 1 | 4 
30 | 4 | 0.250213726423681 | 4.02592384820881 | 1 | 4 
30 | 4 | 0.497460773680359 | 8.00411400658722 | 1 | 8 
42 | 3 | 0.571454062592238 | 33.6117414945302 | 1 | 34 
42 | 3 | 0.00338772451505065 | 0.199258922297338 | 1 | 0 
42 | 3 | 0.139226260595024 | 8.18899958317244 | 1 | 8 

同じ構成(M、N)は、入力テーブルに数回発生した場合、クエリは失敗しますように、私はそれに主キー制約を追加しますため

ALTER TABLE inputs ADD PRIMARY KEY (m, n); 
+0

ニース!問題を超えた解を作成し、同時に複数の固有の(m、n)ペアの問題を解決しました。選択的な切り上げ/切り捨てロジックは素敵です。 – Sim

-1
create table inputs (m int,n int); 
insert into inputs (m,n) values (100,10); 

select  i-lag(i,1,0) over (order by i) as i 

from  (select  * 
      from  (select  i 
         from  generate_series (1,(select m from inputs)) as gs(i) 
         order by random() 
         limit  (select n from inputs)-1 
         ) t 

      union all 

      select  100 
      ) t 

order by i 

サンプル結果

+----+ 
| i | 
+----+ 
| 1 | 
+----+ 
| 1 | 
+----+ 
| 2 | 
+----+ 
| 3 | 
+----+ 
| 4 | 
+----+ 
| 12 | 
+----+ 
| 13 | 
+----+ 
| 16 | 
+----+ 
| 18 | 
+----+ 
| 30 | 
+----+ 

非常に限られたアクセス、これだけのアイデア -

  • 最初のm-1番号 UNION ALL N

  • 注文番号

  • 計算距離を選択してランダム

    によって
  • を数値1..nの-1

  • 注文を生成次の2つの数字の間(LAG - 最初の数字は0を前の数字として使用)

+0

最後にコードを書くことができました。見てください。 –

2

これは(Postgresの)それを行うようだ:値(上記の例では100)の倍の大半合計がすでに6または7の後に達したため

with recursive inputs (n,m) as (
    values (10,100) 
), worker (i, total, rn) as (

    select val, val as total, 1 as rn 
    from ( 
    select floor(random() * (m/n - 1) + 1) 
    from inputs 
    ) as x (val) 

    union all 

    select c.val, p.total + c.val, p.rn + 1 
    from worker p 
    join lateral (
     select floor(random() * (i.m - p.total - 1) + 1) 
     from inputs i 
    ) c (val) on p.rn < (select n from inputs) 
) 
select * 
from worker 
order by rn; 

しかし、これは不正行為と見なされる可能性があります行(時には時には後で)。つまり、最後の「ランダムな」数字はそれ以上ランダムではありません。

良い結果の1つは次のとおりです。

i | total | rn 
----+-------+--- 
    3 |  3 | 1 
    1 |  4 | 2 
40 | 44 | 3 
33 | 77 | 4 
11 | 88 | 5 
    2 | 90 | 6 
    4 | 94 | 7 
    3 | 97 | 8 
    2 | 99 | 9 
    1 | 100 | 10 

しかし、時には、それは同じくらい悪いです:

i | total | rn 
----+-------+--- 
    7 |  7 | 1 
59 | 66 | 2 
23 | 89 | 3 
10 | 99 | 4 
    1 | 100 | 5 
    0 | 100 | 6 
    0 | 100 | 7 
    0 | 100 | 8 
    0 | 100 | 9 
    0 | 100 | 10 

しかし、私はランダムな値が一意である必要がありますどのような要件を見ていません。

オンライン例:http://rextester.com/VRBV22166

+0

ここでは横方向の接合を大いに利用しています。私は解決策の傍受が好きです。彼の解は最後に0の累積を示さないので、私はFabianの試みに答えました。 – Sim

関連する問題