给钱算法

时间:2018-04-15 08:40:00

标签: algorithm kotlin

我必须在我的应用程序中解决这个问题:卖家有一个他在HashMap中所有monney的钱包(价值和数量)。

例如:

  • < 5000,18> 18张50欧元的门票
  • < 1000,2> 2张10欧元的门票
  • < 500,1> 2张5欧元的门票
  • < 200,5> 5欧元2欧元的硬币
  • < 10,3> 3个10美分硬币
  • ...

用户向卖家付款,如果计数不好,卖家必须取钱。我必须制作一个算法,给出最少的硬币以获得完全相同的值(在散列图中)。

例如我必须回馈 26欧元所以我必须提供2张10欧元和3欧元2欧元的门票,并且返回的hashmap必须是这样的:

  • < 1000,2>
  • < 200,3>

这是问题所在,因为如果算法采用第一个元素而不知道是否可以拥有正确的帐户,那么它将提供2张10欧元的票,然后在1张5票之后以及之后被阻止因为给予2欧元更多给你总和27。

如何获得良好的算法呢?

我试试这个:

for ((value, quantity) in my_wallet_map){
      var q = quantity
      while (q > 0){
          if (!isPossibleToPayFromBelow(value, amount, my_wallet_map)){
          what_to_pay_map[value] = (what_to_pay_map[value] ?: 0.toLong()) + 1.toLong()
          my_wallet_map[value] = (my_wallet_map[value] ?: 0.toLong()) - 1.toLong()
          amount -= value
          }
          if (calculTotalFromHashMapInLong(what_to_pay_map) == amount_no_modified)
            break
          q -= 1.toLong()
          }
          if (calculTotalFromHashMapInLong(what_to_pay_map) == amount_no_modified)
                break
}

使用功能:

  fun calculTotalFromHashMapInLong(map: HashMap<Long, Long>): Long{
      var total: Long = 0
      for ((value, quantity) in map)
          total += value * quantity
      return total
  }


  fun isPossibleToPayFromBelow(v: Long, t: Long, map: SortedMap<Long, Long>): Boolean{
      val new_map = map.toSortedMap(reverseOrder())

      var total: Long = 0.toLong()

      for ((value, quantity) in new_map){
          if (quantity != 0.toLong() && value < v) {
              total += (value * quantity)
          }
      }
      return total > t
  }

但结果并不好。我尝试了很多东西。

3 个答案:

答案 0 :(得分:3)

据我所知,对此没有完美而快速的解决方案。 但是,你可以先尝试一个贪婪的算法,如果它失败了,你可以尝试一个精确的算法(不幸的是,这可能非常慢)。

对不起,但我不太了解Kotlin,但是因为你只想要一个算法,我用Python编写了pyseonics最少的东西,所以你可以很容易地把它转换成科特林。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Give back money algorithm."""

DEBUG = True

def print_debug(msg):
    """Prints if DEBUG is set to True."""
    if DEBUG:
        print("    --> {}".format(msg))

# Works great with a structure
# cash_list = [[value_of_coin_1, number_of_coins_1],
#              [value_of_coin_2, number_of_coins_2],
#              ...]
# sorted desc. on value
def greedy(cash_list, value):
    """Tries to gather the requested sum by taking each time the largest
    "coins" avaible."""
    give_cash_list = []
    for (v, n) in cash_list:
        n_of_v = min(n, value // v)
        give_cash_list.append([v, n_of_v])
        value -= v * n_of_v
        if value == 0:
            return give_cash_list
    return None

def exact(cash_list, value):
    """Examines all the possible ways to gather the requested sum."""
    pre_solution_pool = [[[], 0]]
    final_solution_pool = []
    for (v, n) in cash_list:
        new_pre_solution_pool = []
        for (p_s, p_v) in pre_solution_pool:
            new_pre_solution_pool.append([p_s, p_v])  # 0 of the new coin
            for j in range(1, min(n, (value - p_v) // v) + 1):
                new_pre_solution = [p_s + [[v, j]], p_v + v * j]
                if new_pre_solution[1] == value:
                    final_solution_pool.append(new_pre_solution[0])
                else:
                    new_pre_solution_pool.append(new_pre_solution)
        pre_solution_pool = new_pre_solution_pool
    return final_solution_pool

def nb_of_coins(solution):
    """Returns the number of coins of a solution."""
    return sum([n for v, n in solution])

def best_exact(final_solution_pool):
    """Returns the best solution from the exact algorithm."""
    if final_solution_pool:
        return sorted(final_solution_pool, key=nb_of_coins)[0]
    else:
        return None

def compute_new_cash_list(cash_list, give_cash_list):
    """Returns the new cash_list where coins of give_cash_list have been
    removed."""
    new_cash_list = []
    (m, n) = (len(cash_list), len(give_cash_list))
    (i, j) = (0, 0)
    while i < m and j < n:
        (a, b) = cash_list[i]
        (c, d) = give_cash_list[j]
        if a == c:
            new_cash_list.append([a, b - d])
            i += 1
            j += 1
        else:
            new_cash_list.append([a, b])
            i += 1
    return new_cash_list

def give_back(cash_list, value):
    """Best of the two worlds."""
    g = greedy(cash_list, value)
    if g is not None:
        return g, compute_new_cash_list(cash_list, g)
    print_debug("Greedy failed for {} : {}".format(cash_list, value))
    e = best_exact(exact(cash_list, value))
    if e is not None:
        return e, compute_new_cash_list(cash_list, e)
    print_debug("Exact failed for {} : {}".format(cash_list, value))
    return None, cash_list

def main():
    """Launcher."""
    my_cash_list = [[5000, 18], [1000, 2], [500, 1], [200, 5], [10, 3]]
    my_value = 2600
    print(give_back(my_cash_list, my_value))

if __name__ == "__main__":
    main()

我希望它有所帮助。随意提出任何问题!

答案 1 :(得分:1)

贪婪算法的问题

想象一下,你有以下笔记

|-----|------|
|count|value |
|-----|------|
|    1|20.00 |
|    2|10.00 |
|    3| 5.00 |
|    3| 2.00 |
|-----|------|

你想得到21.00然后贪婪算法需要20并且卡住了。在这种情况下,您必须尝试所有组合。

替代解决方案

蛮力方法可以起作用,假设小袋中没有太多硬币,否则尝试的组合会很快变大。

// Sorting the map forces an order
val money = mapOf(
        5000 to 18,
        1000 to 2,
        500 to 1,
        200 to 5,
        10 to 3
).toSortedMap()

// The target amount to find a way of making
val target = 2600

val options = money.asSequence().fold(listOf(listOf<Int>())) { acc, (value, count) ->
    // For each denomination add all the possible counts
    acc.flatMap { option -> (0..count).map { amount -> option + amount } }
}.map { it.zip(money.keys) }

val result = options
    // Only use the counts that give the right change
    .filter { it.map { (a, b) -> a * b }.sum() == target }
    // Find the least amount of notes
    .minBy { it.map { it.first }.sum() }

if (result == null) {
    println("Can't make the change")
} else {
    println(result)
}

可能的优化

您可以限制选项,以便只创建&lt; = target的那些,我没有这样做以保持代码简短,并且对于您提供的数字,完整的组合集仍然很小。< / p>

答案 2 :(得分:0)

您可以使用简单的动态编程算法来查找生成更改所需的最小硬币数。

val coins=listOf(1,2,5,10)

tailrec fun change(target: Int): Int = when (target){
    0 -> 0
    else -> coins.filter{ it <= target}.map { change(target-it) + 1 }.min()!!
}