使用线性最小二乘匹配相似的数字集?

时间:2011-01-04 22:07:08

标签: sql

考虑一下你是否有两组数字:

组A:

10
20
30
40

另一组更大的数字:

SETB:

15
20
35
40
50

目标是从两组中较大的一组中消除项目,这样两组中的项目数相同,并且您将留下尽可能“接近”的组。我有一个想法,我可以尝试每个项目的所有对,并最小化差异的平方。例如,我认为上述解决方案将是:

SetA  SetB   Diff  DiffSquared
10    15     5     25
20    20     0     0 
30    35     5     25
40    40     0     0

DiffSquaredSum == 50

这是解决方案,因为这种组合将为您提供最小的DiffSquaredSum。我并不真正关心项目的配对,主要是将较大的设置修剪为一组相同数量的项目,以及项目尽可能靠近的地方。我意识到我可以使用线性最小二乘的概念来解决这个问题,但我不确定在编码时我会从哪里开始。我更喜欢使用SQL,但我并不关心生成一个精确的解决方案,因为我可以了解一般的方法,因此我可以判断它有多难。

我认为这基本上会涉及尝试两组的所有可能组合:

SetA  SetB   Diff  DiffSquared
10    15     5     25
20    20     0     0 
30    35     5     25
40    50     10    100

SetA  SetB   Diff  DiffSquared
10    15     5     25
20    20     0     0 
30    40     10    100
40    50     10    100

等。然后选择DiffSquared最小和的组。我可能首先为SetA生成一个row_number,然后为SetB生成一个row_number,然后加入它,这恰好给出了答案,但这只是巧合:

Row#    SetA  SetB
1        10    15
2        20    20 
3        30    35
4        40    40 
5       NULL   50

为了确保我找到答案,我需要以某种方式重复这样做,并且有一个ID告诉我哪个组是哪个组合,以便我可以对其进行分组,然后查看每个总和。 我不知道如何获得此设置:

Attempt#  Row#   SetA  SetB
1         1       10    15
1         2       20    20 
1         3       30    35
1         4       40    40 
2         1       10    15
2         2       20    20 
2         3       30    35
2         4       40    50 
3         1       10    15
3         2       20    20 
3         3       30    40
3         4       40    50 
etc....

请注意每次尝试#我已从SetB中删除了其他项目。实际上,SetB可能是许多大于A的项目,因此会消除更多项目,从而有更多可能性。一旦我有了上述内容,我只需添加计算平方差的字段,然后在尝试#上与组进行求和。然后我可以选择具有最小SumDiffSquared的Attempt#。我可能需要将中间结果存储在临时表或表变量中,以便我可以从该ID向后移动以获取成功集中的元素。

所以问题是:如何生成那些“尝试”的所有排列?

2 个答案:

答案 0 :(得分:1)

您可以尝试从setB中获取setA中的所有内容(在您的示例中为20和40),然后将setA中的剩余数字相加(10 + 30 = 40)并尝试找到最接近的匹配总和在setB中具有相同数量的操作数(2,10和30)。 在你的例子中,那将是15 + 35 = 50。

我不确定你为何要平衡分歧。

如果您想要两个集合的所有可能组合,则使用不带ON子句的外部联接。

答案 1 :(得分:1)

你没有提到dbms,所以我使用了Oracle。

with crossjoined as(
  select b
        ,a
        ,abs(a-b) as diff
        ,row_number() over(partition by b order by abs(a-b), a) as rn
    from SetA cross join SetB
)
,ranked as(
  select b,a,diff, row_number() over(order by diff,a) as rn
    from crossjoined
   where rn = 1
)
select b
  from ranked
 where rn <= (select count(*) from SetA)
 order by b;

让我解释它是如何工作的,然后你可以决定我是否正确理解你的问题。该查询包含三个部分:

第1部分“交叉连接” 在这里,我创建了A和B的所有组合。因此,使用您的样本数据,B15与A10,A20,A30,A40等配对。对于每个B,我按差异对所有A进行排名。如果几个A具有相同的差异(如A15和A20对于B15),我首先排序最低的A.这部分查询的示例如下所示。

B    A  DIFF  RN
--   -- ----  --
15   10 5      1
15   20 5      2
15   30 15     3
15   40 25     4
20   20 0      1
20   10 10     2
20   30 10     3
20   40 20     4

第2部分“排名” 查询的这一部分基本上为每个B选择最佳A.这由where rn = 1实现。每个原始B的结果将有一行。行将按差异排序。如果两行具有相同的差异,则具有最小A的行将首先排序。以下是给出样本数据的“交叉连接”和“排名”的完整结果:

B    A  DIFF  RN
--   -- ----  --
20   20    0   1
40   40    0   2
15   10    5   3
35   30    5   4
50   40   10   5

第3部分“主查询” 现在是时候消除在设置为相同数量的项目的项目作为集A.按照你的逻辑寻找一组具有最小的区别,我使用“排名”部分的排名(rn)并选择前N行。其中N是A中的项目数。

如果我能更详细地解释一下,请告诉我。