寻找具有选择条件的最便宜的物品组合

2024-01-11

假设我有 3 个特定商品的卖家。每个卖家存储的此类物品数量不同。该商品也有不同的价格。



Name            Price      Units in storage
Supplier #1     17$        1 Unit
Supplier #2     18$        3 Units
Supplier #3     23$        5 Units
  

如果我没有从同一供应商处订购足够的商品,我必须支付一些额外费用per unit。举例来说,如果我没有订购至少 4 件,则我必须为订购的每件额外支付 5 美元。

一些例子:

如果我想购买 4 件,最优惠的价格是从供应商 #1 and 供应商 #2,而不是从供应商 #3

(17+5)*1 + (18+5)*3 = 91                 <--- Cheaper
            23   *4 = 92

但如果我要买 5 件,全部从供应商3给我一个更好的价格,而不是先买更便宜的,然后从更昂贵的供应商那里买剩下的

(17+5)*1 + (18+5)*3 + (23+5)*1 = 119
                       23   *5 = 115$    <--- Cheaper

问题

记住所有这些...如果我事先知道我想要订购多少商品,那么有什么算法可以找出我可以选择的最佳组合?


如中所述comments https://stackoverflow.com/questions/42556859/finding-cheapest-combination-of-items-with-conditions-on-the-selection/42575089#comment72248426_42556859,您可以为此使用图搜索算法,例如迪杰斯特拉算法 https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm。也可以使用A* https://en.wikipedia.org/wiki/A*_search_algorithm,但为了做到这一点,您需要一个良好的启发式函数。使用最低价格可能可行,但现在我们还是坚持使用 Dijkstra's。

图中的一个节点表示为一个元组(cost, num, counts), where cost显然是成本,num购买的商品总数,以及counts每个卖家的商品数量明细。和cost作为元组中的第一个元素,成本最低的项目将始终位于元组的前面heap。如果该卖家的当前数量低于最小值,我们可以通过添加费用来处理“额外费用”,并在达到该最小值后再次减去该费用。

这是 Python 中的一个简单实现。

import heapq

def find_best(goal, num_cheap, pay_extra, price, items):
    # state is tuple (cost, num, state)
    heap = [(0, 0, tuple((seller, 0) for seller in price))]
    visited = set()

    while heap:
        cost, num, counts = heapq.heappop(heap)
        if (cost, num, counts) in visited:
            continue  # already seen this combination
        visited.add((cost, num, counts))

        if num == goal:  # found one!
            yield (cost, num, counts)

        for seller, count in counts:
            if count < items[seller]:
                new_cost = cost + price[seller]  # increase cost
                if count + 1 < num_cheap: new_cost += pay_extra  # pay extra :(
                if count + 1 == num_cheap: new_cost -= (num_cheap - 1) * pay_extra  # discount! :)
                new_counts = tuple((s, c + 1 if s == seller else c) for s, c in counts)
                heapq.heappush(heap, (new_cost, num+1, new_counts))  # push to heap

上面是一个生成器函数,即您可以使用next(find_best(...))找到最佳组合,或迭代所有组合:

price = {1: 17, 2: 18, 3: 23}
items = {1: 1, 2: 3, 3: 5}
for best in find_best(5, 4, 5, price, items):
    print(best)

正如我们所看到的,购买五件物品有一个更便宜的解决方案:

(114, 5, ((1, 1), (2, 0), (3, 4)))
(115, 5, ((1, 0), (2, 0), (3, 5)))
(115, 5, ((1, 0), (2, 1), (3, 4)))
(119, 5, ((1, 1), (2, 3), (3, 1)))
(124, 5, ((1, 1), (2, 2), (3, 2)))
(125, 5, ((1, 0), (2, 3), (3, 2)))
(129, 5, ((1, 1), (2, 1), (3, 3)))
(130, 5, ((1, 0), (2, 2), (3, 3)))

更新 1:虽然上面的示例工作正常,但是can在失败的情况下,因为一旦我们达到最小数量就减去额外的成本意味着我们可以具有负成本的边,这可能是 Dijkstra 的一个问题。或者,我们可以在一个“操作”中一次性添加所有四个元素。为此,将算法的内部部分替换为:

            if count < items[seller]:
                def buy(n, extra):  # inner function to avoid code duplication
                    new_cost = cost + (price[seller] + extra) * n
                    new_counts = tuple((s, c + n if s == seller else c) for s, c in counts)
                    heapq.heappush(heap, (new_cost, num + n, new_counts))

                if count == 0 and items[seller] >= num_cheap:
                    buy(num_cheap, 0)     # buy num_cheap in bulk
                if count < num_cheap - 1: # do not buy single item \
                    buy(1, pay_extra)     #   when just 1 lower than num_cheap!
                if count >= num_cheap:
                    buy(1, 0)             # buy with no extra cost

更新 2:此外,由于将商品添加到“路径”的顺序并不重要,因此我们可以将卖家限制为不在当前卖家之前的卖家。我们可以添加for seller, count in counts:循环到他的:

        used_sellers = [i for i, (_, c) in enumerate(counts) if c > 0]
        min_sellers = used_sellers[0] if used_sellers else 0
        for i in range(min_sellers, len(counts)):
            seller, count = counts[i]

通过这两项改进,探索图中的状态会寻找next(find_best(5, 4, 5, price, items))像这样(点击放大):

请注意,有许多状态“低于”目标状态,其成本要差得多。这是因为这些是已添加到队列中的所有状态,并且对于每个状态,前驱状态仍然优于最佳状态,因此它们被扩展并添加到队列中,但从未真正从队列中弹出。其中许多可能可以通过使用带有启发式函数的 A* 来消除,例如items_left * min_price.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

寻找具有选择条件的最便宜的物品组合 的相关文章

  • DPLL算法定义

    我在理解 DPLL 算法时遇到一些问题 我想知道是否有人可以向我解释它 因为我认为我的理解是不正确的 我理解的方式是 我采用一些文字集 如果每个子句都为真 则模型为真 但如果某些子句为假 则模型为假 我通过查找单元子句递归地检查模型 如果有
  • 如何计算python 2D散点占用面积

    我使用 matplotlib 绘制了这两个 2000 个点的序列 从图片上看 前2000点占用的面积比后2000点要小 但如果我想定量计算2000个点的第一序列和第二序列占用了多少面积 该怎么办 我真的很感谢任何帮助 建议或意见 非常感谢
  • 如何验证无锁算法?

    从理论上讲 至少应该可以对无锁算法进行暴力验证 只有这么多的函数调用组合 是否有任何工具或正式推理过程可以实际证明无锁算法是正确的 理想情况下它还应该能够检查竞争条件和 ABA 问题 注意 如果你知道一种方法来证明一点 例如 只证明它不受
  • 计算 Adamic-Adar 的快速算法

    我正在研究图形分析 我想计算一个 N N 相似度矩阵 其中包含每两个顶点之间的 Adamic Adar 相似度 为了概述 Adamic Adar 让我从以下介绍开始 给定邻接矩阵A无向图的G CN是两个顶点的所有公共邻居的集合x y 两个顶
  • 算法 - 如何有效删除列表中的重复元素?

    有一个list L 它包含以下元素任意类型each 如何有效删除此类列表中的所有重复元素 必须保留订单 只需要一个算法 因此不允许导入任何外部库 相关问题 在Python中 从列表中删除重复项以使所有元素都是唯一的最快算法是什么在维持秩序的
  • 基数首先排序最重要的还是最不重要的,哪个更快?

    我一直在研究基数排序实现 到目前为止粘贴在下面的代码 代码是用 Java 编写的 但在 C C 中应该也能正常工作 正如您从实现中看到的 我首先执行最高有效位 即整数的第 31 位 这似乎更快 因为一旦子组完成 就不再需要迭代 例如 打个比
  • 地图应用的聚类算法

    我正在研究地图上的聚类点 纬度 经度 对于快速且可扩展的合适算法有什么建议吗 更具体地说 我有一系列纬度 经度坐标和一个地图视口 我正在尝试将靠近的点聚集在一起以消除混乱 我已经有了解决问题的方法 see here http bouldr
  • 优化 HTML 属性压缩顺序

    我在某处读到 按一定顺序组织 HTML 属性可以提高 HTML 文档的压缩率 我想我是从 Google 或 Yahoo 推荐的更快网站上读到这篇文章的 如果我没记错的话 建议是将最常见的属性放在第一位 例如id等 然后将其余的按字母顺序排列
  • 为什么我的 Project Euler Problem 12 算法这么慢?

    我已经在 Scala 中为 PE P12 创建了解决方案 但速度非常非常慢 有人可以告诉我为什么吗 如何优化这个 calculateDevisors 简单的方法和calculateNumberOfDivisors 除数函数具有相同的速度 i
  • 找到不(必要)与二进制矩阵中的图像边界对齐的最大矩形

    我在用这个解决方案 https stackoverflow com questions 2478447 find largest rectangle containing only zeros in an nn binary matrix在
  • 为什么《破解编码面试》这个例子的时间复杂度是O(k c^k)?

    该问题来自 破解编码面试 第 6 版 问题 V1 11 以下代码打印长度为 k 的所有字符串 其中字符 是按排序顺序排列的 它通过生成所有长度的字符串来做到这一点 k 然后检查每个是否已排序 什么是运行时间 package QVI 11 P
  • 数组中最远的相等元素

    假设你有一个未排序的数组 你如何找到两个相等的元素 使它们成为数组中最远的元素 例如8 7 3 4 7 5 3 9 3 7 9 0ans 将是7 9 7 1 8 我想到了以下几点 initialise max 0 using hashing
  • 如何按总和的顺序迭代大量整数元组?

    我在用着itertools combinations http docs python org 2 library itertools html itertools combinations迭代整数元组 我对元组感兴趣最低总和满足一些条件
  • 查找文本中所有关键字的有效算法

    我有很多字符串 其中包含许多不同拼写的文本 我通过搜索关键字来标记这些字符串 如果找到关键字 我将使用该关键字的关联文本 假设搜索字符串可以包含文本 schw schwa 和 施瓦茨 我有三个关键字 全部解析为文本 schwarz 现在我正
  • 序列和与 GCD

    大约一个月前 我在编程挑战中遇到了这个问题 但社论尚未发布 所以我在这里问 有一个大小为 N 的数组 A 求 A 的 K 个长度子序列的总和 GCD Example 如果 A 1 2 3 且 K 2 1 2 3 总和 1 GCD 3 1 3
  • 素数生成器算法

    我一直在尝试解决素数生成算法的SPOJ问题 这是问题 彼得想为他的密码系统生成一些素数 帮助 他 你的任务是生成两个给定之间的所有素数 数字 Input 输入以单行中测试用例的数量 t 开始 t Output 对于每个测试用例 打印所有素数
  • 线段树java实现[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 你知道 二进制 的良好实现吗线段树 http en wikipedia org wiki Segmen
  • 关于Marching Cubes算法的澄清

    关于Marching Cubes 我对其算法和实现有一些疑问 我已经阅读了 Marching Cubes 的 Paul Bourke 优秀文章以及网站上可用的源代码 但是 我在理解以及如何以自己的方式实现算法方面仍然遇到了一些问题 问题如下
  • 数独算法,暴力破解[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我正在尝试
  • 我应该对算法使用递归还是记忆化?

    如果我可以选择使用递归或记忆来解决问题 我应该使用哪一个 换句话说 如果它们都是可行的解决方案 因为它们提供了正确的输出并且可以在我正在使用的代码中合理地表达 那么我什么时候会使用其中一个而不是另一个 它们并不相互排斥 您可以同时使用它们

随机推荐

  • 奥利奥中相同的 ANDROID_ID

    As per Android 8 0 行为变更 对于安装在运行 Android 8 0 的设备上的应用程序 ANDROID ID 的值现在按应用程序签名密钥以及每个用户限定范围 ANDROID ID 的值对于应用签名密钥 用户和设备的每个组
  • Linq-to-Entities 查询中的动态条件

    我正在尝试将一些直接构建 SQL 查询的旧代码转换为实体框架 并遇到了许多人似乎都遇到的问题 从围绕该主题的大量问题来看 如何在 linq 中表达动态 where 条件 我如何用 linq 查询表达以下代码 switch status ca
  • Cypress - 为所有 XHR 请求添加自定义标头

    我注意到X CSRFToken对于从被测应用程序触发的所有 XHR 请求 标头在测试之间被删除 我不确定是否保留此标头 因为我已经通过以下方式保留了 CookieCypress Cookies preserveOnce sessionid
  • glmmTMB 上的计划对比

    如果这是一个重复的问题 我们深表歉意 许多人发帖寻找一种方法来对 glmmTMB 中的条件模型 固定因子 进行事后分析 我想在某些组之间进行有计划的对比 而不是测试每个成对比较 例如 Tukey 下面的代码在 lmm 的 nlme lme
  • ASP.NET Web Api 2 - 子域属性路由

    我一直在使用属性路由 http attributerouting net 在我的 MVC 应用程序中已经有一段时间了 然而 它始终缺少的一件事是 Web Api 中的子域路由 该库中的其他功能可与 MVC 配合使用 但不能与 Web Api
  • 从 phantomjs“SyntaxError: Parse error”消息获取更多信息

    我有一个很长的剧本 不是我写的 当我运行它时 我得到 phantomjs file js SyntaxError Parse error 我查看了手册和帮助 我能想到的最好的办法是 phantomjs debug yes file js i
  • jQuery,检查是否选择了所有单选按钮组

    我有几个单选按钮组 当检查它们时我需要运行一个脚本 我使用以下脚本来检查其中之一是否被选中 如果没有 则将其着色 我该如何编写代码 以便在检查所有单选按钮组时 然后运行脚本 检查单选按钮组是否被选中的代码 aQuestion each fu
  • Doctrine/Symfony 查询生成器在左连接上添加选择

    我有一个与作者表相关的帖子表 这两个表都与第三个表 喜欢 相关 该表指示哪些用户喜欢哪些帖子 我想选择作者和喜欢的帖子 但不知道如何在获取结果后访问连接的对象 我的查询生成器如下所示 result em gt createQueryBuil
  • 如何使用原始转义序列解析字符串?

    假设有2个字符串 string parse const string s how to write this function int main string s1 R hello n this is a string with escap
  • akka-stream + akka-http 生命周期

    TLDR 当我有一个传出的 http 请求作为流的一部分时 每个请求实现一个流 即使用短期流 还是跨请求使用单个流实现更好 详细信息 我有一个典型的服务 它接受 HTTP 请求 将其分散到多个第三方下游服务 不受我控制 并在将结果发送回之前
  • 使用与领域实体的一对一接口是好还是坏做法?为什么?

    我在我开发的一些 DDD 企业应用程序中看到的一件事是使用与域实体相同的接口 以及属性和功能的一对一映射 事实上 域对象始终通过其一对一接口来使用 并且所有域实体都具有这种风格的一对一接口 例如 域对象帐户 public class Acc
  • 在 Spark Scala 中读取二进制文件

    我需要从二进制文件中提取数据 I used binaryRecords并得到RDD Array Byte 从这里我想将每条记录解析为case class Field1 Int Filed2 Short Field3 Long 我怎样才能做到
  • Html5 data-* 与 asp.net mvc TextboxFor html 属性

    我该如何添加data 使用 TextboxFor 的 html 属性 这就是我目前所拥有的 Html TextBoxFor model gt model Country CountryName new data url Url Action
  • .js 文件可以“包含”另一个 .js 文件吗[重复]

    这个问题在这里已经有答案了 在 PhP 中你可以 在 html 中 你可以使用
  • Jenkins 构建抛出内存不足错误

    我们让 Jenkins 在 ec2 实例上运行 在进行构建时 我们看到以下错误 17 29 39 149 INFO org gradle api Project OpenJDK 64 Bit Server VM warning INFO o
  • 使用 @RequestMapping 匹配 URL 模式

    它非常类似于this https stackoverflow com questions 14856149 spring request mapping matching with url pattern问题 但我只是不知道如何匹配 url
  • python subprocess.call() 找不到 Windows Bash.exe

    我有一个程序 它从另一个在 Linux 的新 Windows 子系统上运行的程序获取输出 我编写了一个从 Windows 系统运行的 python 程序 但将使用 python subprocess 模块执行 linux 程序 如果这令人困
  • 哪一段代码性能更好?

    我正在审查一些代码 用于将一些文本转换为MD5 Hash 效果很好 它用于创建一个MD5Hhash for a 头像 头像 这里是 static MD5CryptoServiceProvider md5CryptoServiceProvid
  • django 从连接到任何网络的任何计算机访问本地主机

    我有一个 Django 项目 正在 localhost 8000 上运行 并且运行良好 现在我希望它可以从连接到其他网络的任何计算机进行访问 做了一些谷歌 我发现我可以通过从路由器设置端口转发来做到这一点 我有一个 tplink 路由器 我
  • 寻找具有选择条件的最便宜的物品组合

    假设我有 3 个特定商品的卖家 每个卖家存储的此类物品数量不同 该商品也有不同的价格 Name Price Units in storage Supplier 1 17 1 Unit Supplier 2 18 3 Units Suppli