【问题标题】:find a combination of numbers that equal a given sum from a single column in mysql table从 mysql 表中的单个列中找到等于给定总和的数字组合
【发布时间】:2015-09-17 02:35:04
【问题描述】:

我有一个 MySQL 表包,其中包含字段 id, max_post。 max_post 中的值包含

  1,2,3,4,5,10,20,30,40,50,100,200,500,1000,2000

我想找到最合适的包裹。比如我输入230,那么应该 消除一切并选择 200 和 30 包。我想使用 SQL 查询得到结果。

【问题讨论】:

  • 您的示例表明230 应该对应于 200,30,但是200,20,10 呢?您是否正在明确寻找最小的列表?那么可以从5,24,3 生成的值7 呢?
  • 还是值4831,2,3,4,5,10,20,30,40,50,100,200 = 465,下一个可能的值是500;你选择500 是最接近的,还是1,2,3,4,5,10,20,30,40,50,100,200 是小于或等于你的目标的最大可能值?或者你可以多次使用一个数字来制作一个加起来正好是 465 的列表?
  • 组合中的最大数量为 3 或 4,并且不能重复。
  • 提到的所有其他歧义呢? 230 = 200,20,10200,30 是否一样,如果不是,应该使用什么规则从所有匹配项列表中选择一个可能的匹配项? 7 = 5,24,3 或两者兼而有之? 483 = 200,100,50,40 还是 500
  • 最少计数组合。即200,30。如果多于一种组合采取任何一种。对于 483 ,我们选择 200,100,50,40

标签: php mysql sql codeigniter phpmyadmin


【解决方案1】:

这是一个装箱问题的例子。基本上,解决它的唯一方法是显式。

您可以通过显式连接获得所有此类组合的列表:

select p1.max_post, p2.max_post, p3.max_post, p4.max_post
from packages p1 left join
     packages p2
     on p1.max_post > p2.max_post left join
     packages p3
     on p2.max_post > p3.max_post left join
     packages p4
     on p3.max_post > p4.max_post
where (p1.max_post + coalesce(p2.max_post, 0) + coalesce(p3.max_post, 0) +
       coalesce(p4.max_post, 0)
      ) = 230
order by (p2.max_post is null) desc,
         (p3.max_post is null) desc,
         (p4.max_post is null) desc

order by 将“较短”列表放在首位。如果您愿意,可以添加limit

注意:这实际上是在表中的值之间创建四次笛卡尔积。随着表大小的增加,执行时间也会增加。

【讨论】:

  • 无法解决483 => 200,100,50,40;以目前的形式。
  • 我喜欢这种直截了当的解决方案。不过,考虑到外部联接在查询中的工作方式存在一个小错误。我添加了一个单独的答案,因为我发现这个问题很难在评论中解释。
  • @ThorstenKettner 。 . .您的解决方案更适合最多 4 个数字。我真的在想 4 个数字,不应该在查询中包含 left joins。
【解决方案2】:

满足您的偏好的替代方案483 => 200,100,50,40

SELECT
    target.max_post,
    p1.id,
    p1.max_post,
    p2.id,
    p2.max_post,
    p3.id,
    p3.max_post,
    p4.id,
    p4.max_post
FROM
(
    SELECT 483 AS max_post
)
    target
LEFT JOIN
    packages p1
        ON  p1.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )
LEFT JOIN
    packages p2
        ON  p2.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post - p1.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )
LEFT JOIN
    packages p3
        ON  p3.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post - p1.max_post - p2.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )
LEFT JOIN
    packages p4
        ON  p4.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post - p1.max_post - p2.max_post - p3.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )

它使用重复的相关子查询。对于非常小的packages 表,笛卡尔积搜索可能会更快(虽然不能解决上述情况),对于任何合理大小的任何东西,我都希望相关子查询更快。

对于包含 10 个值的表格
- 笛卡尔积搜索数百种组合(比10 choose 4 = 210 多得多)
- 相关子查询搜索 40 个值 (10x4)

对于包含 100 个值的表
- 笛卡尔积搜索数百万种组合(比100 choose 4 = 3,921,225 多得多)
- 相关子查询搜索 400 个值 (100x4)

【讨论】:

  • 这是个好主意。 (但是,您应该添加条件不要两次选择相同的值,这很快就会完成。)但是必须记住,此查询取决于数据集,因为它始终采用仍然适合总和的最大值.例如,它适用于给定的集合,但如果集合中缺少 10 个,则找不到 60 个的最佳解决方案。 (它会先选择 50,然后最终选择 50-5-4-1,而不是简单地选择 40-20。)
【解决方案3】:

这是 Gordon 查询的更正版本。主要是 Gordon 的查询在有较小值可用时缺少加入空记录。它为 230 找到的最佳匹配是 200-20-5-4,因为最佳解决方案 200-30-null-null 甚至不在我们正在评估的集合中。这是因为 p3 实际上从未外连接到 200-30,因为表中存在值小于 30 的记录。 (外连接意味着当有 no 匹配时添加一个空记录。)

select 
  p1.max_post, p2.max_post, p3.max_post, p4.max_post
from packages p1 
left join (select max_post from packages union all select null) p2
  on (p1.max_post > p2.max_post or p2.max_post is null)  
left join (select max_post from packages union all select null) p3 
  on (p2.max_post > p3.max_post or p3.max_post is null)  
left join (select max_post from packages union all select null) p4 
  on (p3.max_post > p4.max_post or p4.max_post is null)  
where p1.max_post + coalesce(p2.max_post, 0) + coalesce(p3.max_post, 0) + coalesce(p4.max_post, 0) <= 230
order by 
  p1.max_post + coalesce(p2.max_post, 0) + coalesce(p3.max_post, 0) + coalesce(p4.max_post, 0) desc,
  (p2.max_post is null) desc,
  (p3.max_post is null) desc,
  (p4.max_post is null) desc
limit 1;

(这可以通过在查询中添加where max_post &lt;= 230 来略微优化,因此其值本身已经高于所需总和的记录将立即被忽略。)

SQL 小提琴:http://sqlfiddle.com/#!9/5f6d25/18.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-26
    • 1970-01-01
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多