给定 10 个函数 y=a+bx 和 1000 个 (x,y) 数据点四舍五入为整数,如何推导出 10 个最佳 (a,b) 元组?

Posted

技术标签:

【中文标题】给定 10 个函数 y=a+bx 和 1000 个 (x,y) 数据点四舍五入为整数,如何推导出 10 个最佳 (a,b) 元组?【英文标题】:given 10 functions y=a+bx and 1000's of (x,y) data points rounded to ints, how to derive 10 best (a,b) tuples? 【发布时间】:2012-01-26 09:14:48 【问题描述】:

我们开发的软件可以审计银行向接受信用卡和借记卡的商家收取的费用。我们的客户希望我们告诉他们卡处理器是否过度收费。每笔交易信用卡费用的计算方式如下:

fee = fixed + variable*transaction_price

“收费方案”是一组信用卡使用的一对(fixed, variable),例如“好莱坞第一国民银行发行的万事达商业借记金卡”。我们认为,任何时候使用的不同收费方案都不到 10 种,但我们没有从我们的合作伙伴那里获得完整或最新的收费方案列表。 (是的,我知道由于上限和其他问题,一些“费用方案”比上面的等式更复杂,但我们的交易已知只有a + bx 方案在使用)。

这是我们要解决的问题:我们希望使用有关费用的每笔交易数据来推导出正在使用的费用方案。然后我们可以将该列表与每个客户应该根据他们的银行使用的费用方案进行比较。

我们获得的关于每笔交易的数据是一个数据元组:(card_id, transaction_price, fee)

transaction_pricefee 是整数美分。银行将每笔交易的小数美分滚动,直到累计大于一美分,然后将“四舍五入美分”附加到该交易的费用中。我们无法预测“四舍五入”将附加到哪笔交易。

card_id 标识一组共享相同收费方案的卡。在 10,000 笔交易的典型一天中,可能有数百个独特的card_id。多个card_id 将共享一个收费方案。

我们得到的数据是这样的,我们要弄清楚的是最后两列。

card_id    transaction_price       fee        fixed        variable
=======================================================================
12345      200                     22         ?            ?
67890      300                     21         ?            ?
56789      150                      8         ?            ?
34567      150                      8         ?            ?
34567      150    "rounding cent"-> 9         ?            ?
34567      150                      8         ?            ?

我们想要的最终结果是一个这样的简短列表,其中包含 10 个或更少的条目,显示最适合我们数据的费用方案。像这样:

fee_scheme_id       fixed     variable
======================================
1                      22            0
2                      21            0
3                       ?            ?
4                       ?            ?
...

平均费用约为 8 美分。这意味着四舍五入的美分影响巨大,上述推导需要大量数据。

平均交易额为 125 美分。交易价格总是在 5 美分的边界上。

我们想要一份能够“满足”每位客户每天 3,000 多笔交易中 98% 以上的费用方案的简短列表。如果这些数据不足以达到 98% 的置信度,我们可以使用多天的数据。

由于四舍五入的美分有点随意地应用于每笔交易,这不是一个简单的代数问题。相反,这是一种我不知道如何解决的统计聚类练习。

对于如何解决这个问题有什么建议吗?实现可以是 C# 或 T-SQL,以给定算法最有意义的为准。

【问题讨论】:

成交价格是否有明显规律?这将有助于简化问题 他们总是在 5 美分的界限上。 您是否提前知道收费方案,还是应该由算法推导出这些收费方案?如果您有预定义的费用方案,这将是一个更容易的问题。 @James Thompson - 在某些情况下,我们知道收费方案,而在其他情况下,我们不知道,而且在所有情况下,我们都不相信我们从卡处理商那里获得的信息。 :-) 【参考方案1】:

霍夫变换

从图像角度考虑您的问题:如果您将输入数据绘制在价格与费用的图表上,则每个方案的条目将形成一条直线(四舍五入的美分是噪音)。将绘图的密度图视为图像,任务简化为在图像中寻找直线。这只是Hough transform 的工作。

您基本上可以通过将每笔交易的一条线绘制成可能的固定费用与可能的可变费用的图表来解决此问题,并将它们相交的线的值相加。在实际费用方案的点上,许多线将相交并形成较大的局部最大值。通过检测这个最大值,您可以找到您的收费方案,甚至是收费方案的重要程度。

这种方法肯定会奏效,但可能需要一些时间,具体取决于您想要达到的分辨率。如果计算时间被证明是一个问题,请记住粗略 Hough 空间的 Voronoi 图可用作分类器 - 一旦您将点分类为费用方案,简单的线性回归即可解决您的问题。

【讨论】:

很好的答案。这是您的答案之间的折腾,它给了我对问题空间的最佳概念概述,而@dmckee 的答案提供了更多的实现细节,但在这种情况下,概念上的帮助已经足够好了,所以你得到了 +15。谢谢!【参考方案2】:

考虑到处理查询的存储需求与一天的交易数据是 2 的幂次方,我认为这样的存储不是问题,所以:

First pass:将每个 card_id 的交易按 transaction_price 分组,保留 card_id、transaction_price 和平均费用。这可以在 SQL 中轻松完成。这是假设没有异常值 - 但如果需要,您可以在此阶段之后捕获那些异常值。生成的行数保证不高于原始数据点数。

第二遍:每组遍历这些新数据点(使用光标或在 C# 中)并计算 b 的平均值。在此阶段之后,如果需要,可以再次捕获任何异常值。

第三遍:每组计算 a 的平均值,现在 b 已知。这是基本的 SQL。异常值总是如此

如果您决定在游标中执行第二步,则可以将所有内容填充到存储过程中。

现在可以通过以合理的精度舍入 a 和 b 并再次分组,将使用相同费用方案的不同 card_id 组合并(抱歉,这是错误的词,非英语母语)成为费用方案。

【讨论】:

能否详细说明第二步?当 a 未知时,我绝对没有办法简单地计算 b。 在两个数据点之间,您知道价格差异 (delta x) 和费用差异 (delta y) - 这很容易转换为 b in y=a+bx 好吧,很公平,并没有真正关注每个card_id 相对较多的数据值。因此,您的第二次和第三次传递只是每个card_id 的线性回归。 这就是问题所在:对已知属于同一回归组的数据子集具有一定置信度的线性回归,然后将那些结果相同的回归组组合起来解决方案到解决方案组中。【参考方案3】:

Hough transform 是最通用的答案,尽管我不知道如何在 SQL 中实现它(而不是提取数据并用您选择的通用语言处理它)。

唉,如果您有大量输入数据(1000 个点有点中等大小)并且想要高精度结果(缩放为 size_of_the_input / (rho_precision * theta_precision)),那么简单的版本是 known to be slow。

有一个faster approach based on 2^n-trees,但是网络上很少有可以插入的实现。(我最近在 C++ 中做了一个作为我参与的项目的测试平台。也许我会清理它并将其张贴在某处。)


如果数据有一些额外的顺序,您可能会做得更好(即线段是否形成分段函数?)。


朴素霍夫变换

在 (theta,rho) 空间中定义一个跨越 [-pi,pi) 和 [0,max(hypotenuse(x,y)] 的累加器作为二维数组。

Foreach point in the input data
   Foreach bin in theta
      find the distance rho of the altitude from the origin to 
      a line through (a,y) and making angle theta with the horizontal
      rho = x cos(theta) + y sin(theta)
      and increment the bin (theta,rho) in the accumulator
Find the maximum bin in the accumulator, this 
represents the most line-like structure in the data
if (theta !=0) a = rho/sin(theta); b = -1/tan(theta);

可靠地从一次传递中获得多行需要更多的簿记,但并不难。

您可以通过对候选峰值附近的数据进行平滑并进行拟合来稍微改进结果,以获得子 bin 精度,这应该比使用较小的 bin 更快,并且应该相当顺利地获得“四舍五入”美分的效果。

【讨论】:

【参考方案4】:

您将四舍五入视为计算中的一个重要噪声源,因此我将专注于尽量减少由于该问题引起的噪声。执行此 IMO 的最简单方法是增加样本量。

不要将您的数据视为数千个 y=mx + b (+Rounding) 将您的数据分组为更大的子集:

如果您将 X 笔交易与相同的交易结合起来,并将其视为(X 费用的总和)=(可变费率)*(X 笔交易的总和)+ X(基本费率)(+四舍五入)您的四舍五入数,噪音将很可能会落到路边。

获得足够多的大小为“X”的组,您应该能够得出与实数非常接近的表示。

【讨论】:

以上是关于给定 10 个函数 y=a+bx 和 1000 个 (x,y) 数据点四舍五入为整数,如何推导出 10 个最佳 (a,b) 元组?的主要内容,如果未能解决你的问题,请参考以下文章

hihoCoder #1142 : 三分求极值

Wannafly挑战赛4 A.解方程 (二分)

三次函数的图像怎么画

李超线段树

数百万个 3D 点:如何找到最接近给定点的 10 个?

Python 函数-max()