【问题标题】:Algorithm to minimize total price by using different promotions通过使用不同的促销来最小化总价的算法
【发布时间】:2019-12-13 06:12:19
【问题描述】:

我不知道使用什么标题,但我的问题如下: 我有 X 种产品要购买 - 每一种都需要指定金额。 我还有 4 种促销活动:

  • twoItemPromotion -> 可用于 2 件商品,一件最便宜的折扣为 22%
  • threeItemPromotion -> 可用于 3 件商品,一件最便宜的折扣为 44%
  • fourItemPromotion -> 可用于 4 件商品,一件最便宜的折扣为 66%
  • fiveOrMoreItemPromotion -> 可用于 5 件或更多件商品,一件最便宜的的折扣为 99.9%

我可以根据需要拆分 X 件商品的订单,例如,如果我想购买 10 件商品,则可以将其拆分为 5 个使用 twoItemPromotion 的订单。或者 2 个订单使用 threeItemPromotion 和 1 个使用 FiveOrMoreItemPromotion。

这里我建模了一些值对象来表示项目和促销:

fun main() {
    val itemA = Item(name = "A", price = 1000)
    val itemB = Item(name = "B", price = 1500)
    val itemC = Item(name = "C", price = 2000)
    val itemD = Item(name = "D", price = 1200)
    val itemE = Item(name = "E", price = 1600)
    val itemF = Item(name = "F", price = 1300)
    val itemG = Item(name = "G", price = 2400)
    val itemH = Item(name = "H", price = 800)
    val itemJ = Item(name = "J", price = 1400)
    val itemK = Item(name = "K", price = 1200)

    val itemListToBuy = listOf(itemA, itemB, itemC, itemD, itemE, itemF, itemG, itemH, itemJ, itemK)

    val twoItemPromotion = Promotion(numberOfItems = 2, discountForCheapest = 0.22)
    val threeItemPromotion = Promotion(numberOfItems = 3, discountForCheapest = 0.44)
    val fourItemPromotion = Promotion(numberOfItems = 4, discountForCheapest = 0.66)
    val fiveOrMoreItemPromotion = Promotion(numberOfItems = 5, discountForCheapest = 0.9999)


}

data class Item(val name: String, val price: Int)

data class Promotion(val numberOfItems: Int, val discountForCheapest: Double)

您将如何检索哪些产品与哪些促销活动的最佳组合以及它会节省多少?我很好奇如果我想将它用于例如 500 件左右的物品,那么计算最佳组合的有效方法是什么。

编辑: 澄清促销如何运作: 如果我订购这 3 件商品:

val itemA = Item(name = "A", price = 1000)
val itemB = Item(name = "B", price = 1500)
val itemC = Item(name = "C", price = 2000)

如果没有促销,它将花费1000 + 1500 + 2000 = 4500。 但是考虑到 threeItemPromotion 最便宜的一个 - itemA - 他的价格降低了 44% => itemA成本:1000 * (1-0.44) = 556 和其余商品 - itemBitemC - 价格不受影响,这意味着它们一起花费 556 (cheapest item with discount) + 1500 + 2000 = 4056

编辑 2: 现在我有这个:

    fun main() {
    val itemA = Item(name = "A", price = 1000)
    val itemB = Item(name = "B", price = 1500)
    val itemC = Item(name = "C", price = 2000)
    val itemD = Item(name = "D", price = 1200)
    val itemE = Item(name = "E", price = 1600)
    val itemF = Item(name = "F", price = 1300)
    val itemG = Item(name = "G", price = 2400)
    val itemH = Item(name = "H", price = 800)
    val itemJ = Item(name = "J", price = 1400)
    val itemK = Item(name = "K", price = 1200)

    val itemList = listOf(itemA, itemB, itemC, itemD, itemE, itemF, itemG, itemH, itemJ, itemK)

    val sorted = itemList.sortedBy { it.price }

    val bestResult = mutableListOf(Result(0.0, 0))

    for (i in 0 until sorted.size) {
        var best = Result(
            cost = (sorted.get(i).price) + bestResult.get(i).cost,
            groupSize = 1
        )
        for (s in 2..5) {
            val candidate = Result(cost = getPrice(i - s, sorted), groupSize = s)
            if (candidate.cost < best.cost) {
                best = candidate
            }
        }
        bestResult.add(best)
    }

    bestResult.forEach { println(it) }
}



  fun getPrice(numberOfItems: Int, list: List<Item>): Double {
    val found = mutableListOf<Int?>()
    for(i in 0 until numberOfItems) {
        found.add(list.getOrNull(i)?.price)
    }
    val filterNotNull = found.filterNotNull()
    return when(numberOfItems) {
        2 -> (filterNotNull.get(0) * 0.78 + filterNotNull.get(1))
        3 -> (filterNotNull.get(0) * 0.56 + filterNotNull.get(1) + filterNotNull.get(2))
        4 -> (filterNotNull.get(0) * 0.34 + filterNotNull.get(1) + filterNotNull.get(2) + filterNotNull.get(3))
        5 -> (1.0 + filterNotNull.get(1) + filterNotNull.get(2) + filterNotNull.get(3) + filterNotNull.get(4))
        else -> 1000000000.0
    }
}
data class Item(val name: String, val price: Int)

data class Result(val cost: Double, val groupSize: Int)

但它没有正确计算价格 - 我假设我在 getPrice() 方法中有错误。任何想法如何解决这个问题?

【问题讨论】:

  • 我正在尝试理解这个问题。 “最便宜的”是什么意思?假设我要买两件,第三件可以少 22% 买吗?或者是其他东西?抱歉 - 我不明白。
  • 添加编辑并举例说明它是如何工作的
  • 好的,所以您正在寻找 500 个项目,因为项目的价格不同,结果会随输入而变化,尽管 500 是固定的。您需要准确指定订单中有多少 A、B、C、...。
  • 要搜索的关键词是“动态规划”和“分区问题”。您需要考虑多种选择;您可以使用一些代数限制来减少搜索树(以获得严格的优势),并让您的 DP 回溯确定最佳解决方案。这是技术应用的问题,而不是特定的算法。
  • 您的getPrice 有两个问题浮现在脑海。第一个是您必须获得具有开始和结束的列表范围的价格。第二个是您需要边界检查才能不超出数组。

标签: algorithm kotlin


【解决方案1】:

首先,您需要进行观察。将您的商品从最便宜到最贵排序。

现在假设我有一个声称是最优的分组。查看拥有最便宜商品的组。如果该组的其余部分不是下一个最便宜的项目,我可以交换该组中的哪些项目,直到它不会使情况变得更糟。然后我可以对下一个做同样的事情。

结果是您有一个最佳安排,其中每个组在该列表中是连续的。这种观察极大地减少了您查看的可能性组合爆炸。

现在它是动态规划的一个很好的候选者,如下所示。

item_infos = ((i.price, i.name, i) for i in item_list_to_buy)
item_infos = sorted(item_infos)
best_result = [{cost: 0, group_size: 0}]
for i in range(len(item_infos)):
    best = {cost: item_infos[i][0] + best_result[i].cost, group_size: 1}
    for s in [2, 3, 4, 5]:
        candidate = do calculation of group of size s ending here
        if candidate.cost < best.cost:
            best = candidate
    best_result.append(best)

在您的情况下,您的 item_infos 最终会是:

[
    (800, 'H', itemH)
  , (1000, 'A', itemA)
  , (1200, 'D', itemD)
  , (1200, 'K', itemK)
  , (1300, 'F', itemF)
  , (1400, 'J', itemJ)
  , (1500, 'B', itemB)
  , (1600, 'E', itemE)
  , (2000, 'C', itemC)
  , (2400, 'G', itemG)
]

然后您的 best_result 将是(手动执行此操作的模错误)

[
    {cost: 0, group_size: 0}
  , {cost: 800, group_size: 1}
  , {cost: 1980, group_size: 2}
  , {cost: 2648, group_size: 3}
  , {cost: 3760, group_size: 3}
  , {cost: 4700.8, group_size: 5}
  , {cost: 5901, group_size: 5}
  , {cost: 7256, group_size: 4}
  , {cost: 8449.2, group_size: 5}
  , {cost: 10261.3, group_size: 5}
  , {cost: 12202.2, group_size: 5}
]

现在我们把它读完。

我们希望最后 5 个作为一个组 [J, B, E, C, G]。我们的目标是{cost: 4700.8, group_size: 5},所以我们希望下一个最后 5 个作为一组 [H, A, K, D, F]

该算法在线性时间内运行,因此应该很容易找到 500 个项目的最佳答案。

注意:如果您的促销开始是组合在一起的特定项目组,则此问题从简单到可能 NP 完成...

【讨论】:

  • 我在计算候选人的当前价格时遇到问题...我在 EDIT 2 部分添加了一个 sn-p - 也许您可以指出我有错误的地方,因此价格计算不正确
猜你喜欢
  • 1970-01-01
  • 2013-10-20
  • 1970-01-01
  • 1970-01-01
  • 2015-10-21
  • 1970-01-01
  • 2023-01-15
  • 2014-06-11
  • 1970-01-01
相关资源
最近更新 更多