Cách tìm tất cả các tổ hợp tiền xu khi được cung cấp một số giá trị đô la


114

Tôi đã tìm thấy một đoạn mã mà tôi đã viết để chuẩn bị phỏng vấn vài tháng trước.

Theo nhận xét tôi có, nó đang cố gắng giải quyết vấn đề này:

Cho một số giá trị đô la tính bằng xu (ví dụ: 200 = 2 đô la, 1000 = 10 đô la), hãy tìm tất cả các kết hợp của các đồng tiền tạo nên giá trị đô la. Chỉ được phép sử dụng đồng xu (1 ¢), niken (5 ¢), dimes (10 ¢) và phần tư (25 ¢).

Ví dụ, nếu 100 được đưa ra, câu trả lời sẽ là:

4 quarter(s) 0 dime(s) 0 nickel(s) 0 pennies  
3 quarter(s) 1 dime(s) 0 nickel(s) 15 pennies  
etc.

Tôi tin rằng điều này có thể được giải quyết theo cả hai cách lặp lại và đệ quy. Giải pháp đệ quy của tôi khá lỗi và tôi đã tự hỏi làm thế nào những người khác sẽ giải quyết vấn đề này. Phần khó khăn của vấn đề này là làm cho nó hiệu quả nhất có thể.


6
@akappa: penny = 1 xu; niken = 5 xu; xu = 10 xu; quý = 25 xu :)
codingbear

@John T: mã gôn? Tôi chưa bao giờ nghe nói về thuật ngữ đó! Dù sao, tôi đang hy vọng sẽ nhìn thấy một số câu trả lời thú vị, vì cộng đồng SO có thể giải quyết bất kỳ vấn đề
codingbear

Tôi cũng sẽ cố gắng đăng câu trả lời của mình khi tôi về nhà ... vẫn đang làm việc và tôi không nên dành quá nhiều thời gian cho SO.
codingbear, 09/07/09

1
@blee code golf đề cập đến việc giải quyết một vấn đề với số lượng ký tự ít nhất có thể, bằng ngôn ngữ lập trình bạn chọn. Dưới đây là một số đã được thực hiện trên trang web này: stackoverflow.com/search?q=code+golf
John T

Câu trả lời:


54

Tôi đã xem xét vấn đề này một lần cách đây rất lâu, và bạn có thể đọc bài viết nhỏ của tôi về nó . Đây là nguồn Mathematica .

Bằng cách sử dụng các hàm tạo, bạn có thể nhận được giải pháp thời gian không đổi dạng đóng cho vấn đề. Toán học cụ thể của Graham, Knuth và Patashnik là cuốn sách dành cho điều này, và chứa một cuộc thảo luận khá sâu rộng về vấn đề này. Về cơ bản, bạn xác định một đa thức trong đó hệ số thứ n là số cách thực hiện thay đổi đối với n đô la.

Các trang 4-5 của bài viết chỉ ra cách bạn có thể sử dụng Mathematica (hoặc bất kỳ hệ thống đại số máy tính tiện lợi nào khác) để tính toán câu trả lời cho 10 ^ 10 ^ 6 đô la trong vài giây trong ba dòng mã.

(Và điều này đã đủ lâu trước đó là một vài giây trên Pentium 75Mhz ...)


16
Câu trả lời hay, nhưng có những phân minh nhỏ: lưu ý rằng (1) Điều này cho biết số cách, trong khi vì một số lý do, câu hỏi yêu cầu tập hợp thực tế của tất cả các cách. Tất nhiên, không thể có cách nào để tìm tập hợp trong thời gian đa thức, vì bản thân đầu ra có nhiều mục siêu đa thức (2) Có thể tranh cãi xem một hàm sinh có phải là một "dạng đóng" hay không (xem cuốn sách tuyệt vời Generatingfunctionology : math của Herbert Wilf) . upenn.edu/~wilf/DownldGF.html ) và nếu bạn muốn nói một biểu thức như (1 + √5) ^ n, thì cần Ω (log n) thời gian để tính toán, không phải thời gian không đổi.
ShreevatsaR

Giới thiệu nhẹ nhàng về lập trình động. Ngoài ra, tôi khuyến khích bất kỳ ai có vấn đề về trình tự đọc kỹ thuật tạo hàm .
Colonel Panic

Cảm ơn rất nhiều Andrew ... lời giải thích này đã giúp tôi hiểu rất nhiều ... Gửi bài chức năng scala dưới đây .. nên số một cần nó
Jayaram S

1
Tôi tin rằng câu hỏi ở phần đầu cần được điều chỉnh một chút vì nó hỏi "... sử dụng tiền xu 1-, 10-, 25-, 50- và 100 xu?" Nhưng sau đó ghi lên xác định tập hợp alà miền của fbut a = {1,5,10,25,50,100}. Nên có 5 trong danh sách tiền xu. Nếu không, viết lên là tuyệt vời, cảm ơn!
rbrtl

@rbrtl Chà, bạn nói đúng, cảm ơn vì đã chú ý đến điều đó! Tôi sẽ cập nhật nó…
andrewdotn

42

Lưu ý : Điều này chỉ hiển thị số lượng cách.

Hàm Scala:

def countChange(money: Int, coins: List[Int]): Int =
  if (money == 0) 1
  else if (coins.isEmpty || money < 0) 0
  else countChange(money - coins.head, coins) + countChange(money, coins.tail)

1
Có thực sự là một cách để thay đổi số 0? Tôi đoán không có cách nào để làm điều đó.
Luke

2
Nó bắt nguồn từ số lượng các nghiệm của đa thức n1 * coins(0) + n2 * coins(1) + ... + nN * coins(N-1) = money. Vì vậy, cho money=0coins=List(1,2,5,10)số cho các kết hợp (n1, n2, n3, n4)là 1 và giải pháp là (0, 0, 0, 0).
Kyr

3
Tôi không thể hiểu lý do tại sao triển khai này hoạt động. Ai đó có thể giải thích cho tôi thuật toán đằng sau không?
Adrien Lemaire

3
Đây chắc chắn là câu trả lời chính xác cho vấn đề 3 của bài tập 1 của khóa học scala.
Justin Standard

Tôi tin rằng, nếu money == 0nhưng coins.isEmpty, nó không nên được coi là một sol'n. Do đó, algo có thể được phục vụ tốt hơn nếu coins.isEmpty || money < 0điều kiện là ck'd lần đầu tiên.
juanchito

26

Tôi sẽ ủng hộ một giải pháp đệ quy. Bạn có một số danh sách mệnh giá, nếu mệnh giá nhỏ nhất có thể chia đều bất kỳ số tiền còn lại nào, điều này sẽ hoạt động tốt.

Về cơ bản, bạn chuyển từ mệnh giá lớn nhất đến nhỏ nhất.
Đệ quy,

  1. Bạn có tổng số tiền hiện tại để điền và mệnh giá lớn nhất (còn lại nhiều hơn 1). Nếu chỉ còn 1 mệnh giá duy nhất thì chỉ còn cách điền vào tổng số. Bạn có thể sử dụng 0 đến k bản sao của mệnh giá hiện tại của mình sao cho k * cur mệnh giá <= tổng.
  2. Với 0 đến k, hãy gọi hàm có tổng đã sửa đổi và mệnh giá mới lớn nhất.
  3. Cộng các kết quả từ 0 đến k. Đó là cách bạn có thể điền vào tổng số tiền của mình từ mệnh giá hiện tại trở xuống. Trả lại số này.

Đây là phiên bản python của tôi về vấn đề đã nêu của bạn, với giá 200 xu. Tôi nhận được 1463 cách. Phiên bản này in tất cả các kết hợp và tổng số cuối cùng.

#!/usr/bin/python

# find the number of ways to reach a total with the given number of combinations

cents = 200
denominations = [25, 10, 5, 1]
names = {25: "quarter(s)", 10: "dime(s)", 5 : "nickel(s)", 1 : "pennies"}

def count_combs(left, i, comb, add):
    if add: comb.append(add)
    if left == 0 or (i+1) == len(denominations):
        if (i+1) == len(denominations) and left > 0:
           if left % denominations[i]:
               return 0
           comb.append( (left/denominations[i], demoninations[i]) )
           i += 1
        while i < len(denominations):
            comb.append( (0, denominations[i]) )
            i += 1
        print(" ".join("%d %s" % (n,names[c]) for (n,c) in comb))
        return 1
    cur = denominations[i]
    return sum(count_combs(left-x*cur, i+1, comb[:], (x,cur)) for x in range(0, int(left/cur)+1))

count_combs(cents, 0, [], None)

Có không chạy nó, nhưng bằng cách đi qua logic của bạn, nó có ý nghĩa :)
codingbear

Bạn có thể thay thế hai dòng cuối cùng của hàm bằng "return sum (count_combs (...) cho ...)" - theo cách đó, danh sách hoàn toàn không được hiện thực hóa. :)
Nick Johnson

Cảm ơn vì tiền hỗ trợ. Tôi luôn quan tâm đến các cách để thắt chặt mã.
leif 10/07/09

2
Như đã thảo luận trong một câu hỏi khác , mã này sẽ cung cấp đầu ra không chính xác nếu danh sách denominationskhông có 1giá trị cuối cùng. Bạn có thể thêm một lượng nhỏ mã vào ifkhối trong cùng để sửa nó (như tôi mô tả trong câu trả lời của tôi cho câu hỏi kia).
Blckknght

12

Hàm Scala:

def countChange(money: Int, coins: List[Int]): Int = {

def loop(money: Int, lcoins: List[Int], count: Int): Int = {
  // if there are no more coins or if we run out of money ... return 0 
  if ( lcoins.isEmpty || money < 0) 0
  else{
    if (money == 0 ) count + 1   
/* if the recursive subtraction leads to 0 money left - a prefect division hence return count +1 */
    else
/* keep iterating ... sum over money and the rest of the coins and money - the first item and the full set of coins left*/
      loop(money, lcoins.tail,count) + loop(money - lcoins.head,lcoins, count)
  }
}

val x = loop(money, coins, 0)
Console println x
x
}

Cảm ơn! Đây là một khởi đầu tuyệt vời. Nhưng, tôi nghĩ rằng điều này không thành công khi "tiền" bắt đầu bằng 0 :).
aqn

10

Đây là một số mã C ++ hoàn toàn đơn giản để giải quyết vấn đề yêu cầu tất cả các kết hợp được hiển thị.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("usage: change amount-in-cents\n");
        return 1;
    }

    int total = atoi(argv[1]);

    printf("quarter\tdime\tnickle\tpenny\tto make %d\n", total);

    int combos = 0;

    for (int q = 0; q <= total / 25; q++)
    {
        int total_less_q = total - q * 25;
        for (int d = 0; d <= total_less_q / 10; d++)
        {
            int total_less_q_d = total_less_q - d * 10;
            for (int n = 0; n <= total_less_q_d / 5; n++)
            {
                int p = total_less_q_d - n * 5;
                printf("%d\t%d\t%d\t%d\n", q, d, n, p);
                combos++;
            }
        }
    }

    printf("%d combinations\n", combos);

    return 0;
}

Nhưng tôi khá thú vị về vấn đề phụ chỉ tính số lượng kết hợp. Tôi nghi ngờ có một phương trình dạng đóng cho nó.


9
Chắc chắn đây là C, không phải C ++.
nikhil

1
@George Phillips bạn có thể giải thích không?
Cố gắng vào

Tôi nghĩ nó khá đơn giản. Về cơ bản, ý tưởng là để lặp tất cả các quý (sử dụng 0,1,2 .. max), và sau đó lặp qua tất cả hào dựa trên tư sử dụng, vv ..
Peter Lee

4
Nhược điểm cho giải pháp này là: nếu có 50 xu, 100 xu, tiền xu 500 xu, thì chúng ta phải sử dụng vòng 6 cấp ...
Peter Lee

3
Điều này khá tệ, nếu bạn có một mệnh giá động hoặc bạn sẽ muốn thêm một mệnh giá khác thì điều này sẽ không hoạt động.
shinzou

7

Bài toán con là một bài toán Lập trình động điển hình.

/* Q: Given some dollar value in cents (e.g. 200 = 2 dollars, 1000 = 10 dollars),
      find the number of combinations of coins that make up the dollar value.
      There are only penny, nickel, dime, and quarter.
      (quarter = 25 cents, dime = 10 cents, nickel = 5 cents, penny = 1 cent) */
/* A:
Reference: http://andrew.neitsch.ca/publications/m496pres1.nb.pdf
f(n, k): number of ways of making change for n cents, using only the first
         k+1 types of coins.

          +- 0,                        n < 0 || k < 0
f(n, k) = |- 1,                        n == 0
          +- f(n, k-1) + f(n-C[k], k), else
 */

#include <iostream>
#include <vector>
using namespace std;

int C[] = {1, 5, 10, 25};

// Recursive: very slow, O(2^n)
int f(int n, int k)
{
    if (n < 0 || k < 0)
        return 0;

    if (n == 0)
        return 1;

    return f(n, k-1) + f(n-C[k], k); 
}

// Non-recursive: fast, but still O(nk)
int f_NonRec(int n, int k)
{
    vector<vector<int> > table(n+1, vector<int>(k+1, 1));

    for (int i = 0; i <= n; ++i)
    {
        for (int j = 0; j <= k; ++j)
        {
            if (i < 0 || j < 0) // Impossible, for illustration purpose
            {
                table[i][j] = 0;
            }
            else if (i == 0 || j == 0) // Very Important
            {
                table[i][j] = 1;
            }
            else
            {
                // The recursion. Be careful with the vector boundary
                table[i][j] = table[i][j-1] + 
                    (i < C[j] ? 0 : table[i-C[j]][j]);
            }
        }
    }

    return table[n][k];
}

int main()
{
    cout << f(100, 3) << ", " << f_NonRec(100, 3) << endl;
    cout << f(200, 3) << ", " << f_NonRec(200, 3) << endl;
    cout << f(1000, 3) << ", " << f_NonRec(1000, 3) << endl;

    return 0;
}

Các giải pháp động của bạn yêu cầu k phải là độ dài của C trừ đi 1. hơi khó hiểu. Bạn có thể thay đổi nó một cách dễ dàng để hỗ trợ độ dài thực tế của C.
Idan

7

Mã đang sử dụng Java để giải quyết vấn đề này và nó cũng hoạt động ... Phương pháp này có thể không phải là một ý tưởng hay vì quá nhiều vòng lặp, nhưng nó thực sự là một cách thẳng về phía trước.

public class RepresentCents {

    public static int sum(int n) {

        int count = 0;
        for (int i = 0; i <= n / 25; i++) {
            for (int j = 0; j <= n / 10; j++) {
                for (int k = 0; k <= n / 5; k++) {
                    for (int l = 0; l <= n; l++) {
                        int v = i * 25 + j * 10 + k * 5 + l;
                        if (v == n) {
                            count++;
                        } else if (v > n) {
                            break;
                        }
                    }
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(sum(100));
    }
}

7

Đây là một câu hỏi thực sự cũ, nhưng tôi đã nghĩ ra một giải pháp đệ quy trong java có vẻ nhỏ hơn tất cả các giải pháp khác, vì vậy đây là -

 public static void printAll(int ind, int[] denom,int N,int[] vals){
    if(N==0){
        System.out.println(Arrays.toString(vals));
        return;
    }
    if(ind == (denom.length))return;             
    int currdenom = denom[ind];
    for(int i=0;i<=(N/currdenom);i++){
        vals[ind] = i;
        printAll(ind+1,denom,N-i*currdenom,vals);
    }
 }

Cải tiến:

  public static void printAllCents(int ind, int[] denom,int N,int[] vals){
        if(N==0){
            if(ind < denom.length) {
                for(int i=ind;i<denom.length;i++)
                    vals[i] = 0;
            }
            System.out.println(Arrays.toString(vals));
            return;
        }
        if(ind == (denom.length)) {
            vals[ind-1] = 0;
            return;             
        }

        int currdenom = denom[ind];
        for(int i=0;i<=(N/currdenom);i++){ 
                vals[ind] = i;
                printAllCents(ind+1,denom,N-i*currdenom,vals);
        }
     }

6

Cho C (i, J) là tập hợp các phép tạo ra i xu bằng cách sử dụng các giá trị trong tập J.

Bạn có thể định nghĩa C như sau:

nhập mô tả hình ảnh ở đây

(đầu tiên (J) nhận một cách xác định một phần tử của một tập hợp)

Nó chỉ ra một hàm đệ quy khá tốt ... và hiệu quả hợp lý nếu bạn sử dụng memoization;)


Vâng, đây ("lập trình động", theo một nghĩa nào đó) sẽ là giải pháp tối ưu.
ShreevatsaR 09/07/09

bạn nói đúng: lấy J làm danh sách chứ không phải một tập hợp: sau đó đầu tiên (J) mang đến cho bạn phần tử đầu tiên và J \ first (J) cung cấp cho bạn phần còn lại của danh sách.
akappa, 09/07/09

đây là dạng toán nào?
Muhammad Umer

5

bán hack để giải quyết vấn đề kết hợp duy nhất - thứ tự giảm dần lực lượng:

$ ký hiệu = [1,5,10,25]
def all_combs (sum, last) 
  trả về 1 nếu sum == 0
  trả về $ precision.select {| d | d & le sum && d & le last} .inject (0) {| total, denom |
           tổng + all_combs (sum-denom, denom)}
kết thúc

Điều này sẽ chạy chậm vì nó sẽ không được ghi nhớ, nhưng bạn có ý tưởng.


4
# short and sweet with O(n) table memory    

#include <iostream>
#include <vector>

int count( std::vector<int> s, int n )
{
  std::vector<int> table(n+1,0);

  table[0] = 1;
  for ( auto& k : s )
    for(int j=k; j<=n; ++j)
      table[j] += table[j-k];

  return table[n];
}

int main()
{
  std::cout <<  count({25, 10, 5, 1}, 100) << std::endl;
  return 0;
}

3

Đây là câu trả lời của tôi bằng Python. Nó không sử dụng đệ quy:

def crossprod (list1, list2):
    output = 0
    for i in range(0,len(list1)):
        output += list1[i]*list2[i]

    return output

def breakit(target, coins):
    coinslimit = [(target / coins[i]) for i in range(0,len(coins))]
    count = 0
    temp = []
    for i in range(0,len(coins)):
        temp.append([j for j in range(0,coinslimit[i]+1)])


    r=[[]]
    for x in temp:
        t = []
        for y in x:
            for i in r:
                t.append(i+[y])
        r = t

    for targets in r:
        if crossprod(targets, coins) == target:
            print targets
            count +=1
    return count




if __name__ == "__main__":
    coins = [25,10,5,1]
    target = 78
    print breakit(target, coins)

Ví dụ đầu ra

    ...
    1 ( 10 cents)  2 ( 5 cents)  58 ( 1 cents)  
    4 ( 5 cents)  58 ( 1 cents)  
    1 ( 10 cents)  1 ( 5 cents)  63 ( 1 cents)  
    3 ( 5 cents)  63 ( 1 cents)  
    1 ( 10 cents)  68 ( 1 cents)  
    2 ( 5 cents)  68 ( 1 cents)  
    1 ( 5 cents)  73 ( 1 cents)  
    78 ( 1 cents)  
    Number of solutions =  121

3
var countChange = function (money,coins) {
  function countChangeSub(money,coins,n) {
    if(money==0) return 1;
    if(money<0 || coins.length ==n) return 0;
    return countChangeSub(money-coins[n],coins,n) + countChangeSub(money,coins,n+1);
  }
  return countChangeSub(money,coins,0);
}

2

Cả hai: lặp lại qua tất cả các mệnh giá từ cao đến thấp, lấy một mệnh giá, trừ vào tổng số được yêu cầu, sau đó lặp lại trên phần còn lại (ràng buộc các mệnh giá có sẵn phải bằng hoặc thấp hơn giá trị lặp hiện tại.)


2

Nếu hệ thống tiền tệ cho phép, đơn giản thuật toán tham lam lấy càng nhiều đồng xu càng tốt, bắt đầu với đồng tiền có giá trị cao nhất.

Nếu không, cần phải lập trình động để nhanh chóng tìm ra giải pháp tối ưu vì vấn đề này về cơ bản là vấn đề về cái túi .

Ví dụ: nếu một hệ thống tiền tệ có các đồng tiền {13, 8, 1}:, giải pháp tham lam sẽ tạo ra thay đổi cho 24 là {13, 8, 1, 1, 1}, nhưng giải pháp tối ưu thực sự là{8, 8, 8}

Chỉnh sửa: Tôi nghĩ rằng chúng tôi đang thực hiện thay đổi một cách tối ưu, không liệt kê tất cả các cách để thực hiện thay đổi cho một đô la. Cuộc phỏng vấn gần đây của tôi hỏi làm thế nào để thay đổi vì vậy tôi đã nhảy lên trước trước khi kết thúc để đọc câu hỏi.


vấn đề không nhất thiết phải là một đô la - nó có thể là 2 hoặc 23, vì vậy giải pháp của bạn vẫn là giải pháp đúng duy nhất.
Neil G,

2

Tôi biết đây là một câu hỏi rất cũ. Tôi đã tìm kiếm câu trả lời thích hợp và không thể tìm thấy bất kỳ điều gì đơn giản và thỏa đáng. Đã cho tôi một thời gian nhưng có thể ghi lại một cái gì đó.

function denomination(coins, original_amount){
    var original_amount = original_amount;
    var original_best = [ ];

    for(var i=0;i<coins.length; i++){
      var amount = original_amount;
      var best = [ ];
      var tempBest = [ ]
      while(coins[i]<=amount){
        amount = amount - coins[i];
        best.push(coins[i]);
      }
      if(amount>0 && coins.length>1){
        tempBest = denomination(coins.slice(0,i).concat(coins.slice(i+1,coins.length)), amount);
        //best = best.concat(denomination(coins.splice(i,1), amount));
      }
      if(tempBest.length!=0 || (best.length!=0 && amount==0)){
        best = best.concat(tempBest);
        if(original_best.length==0 ){
          original_best = best
        }else if(original_best.length > best.length ){
          original_best = best;
        }  
      }
    }
    return original_best;  
  }
  denomination( [1,10,3,9] , 19 );

Đây là một giải pháp javascript và sử dụng đệ quy.


Giải pháp này chỉ tìm thấy một mệnh giá. Câu hỏi là để tìm "tất cả" mệnh giá.
heinob

2

Trong ngôn ngữ lập trình Scala, tôi sẽ làm như thế này:

 def countChange(money: Int, coins: List[Int]): Int = {

       money match {
           case 0 => 1
           case x if x < 0 => 0
           case x if x >= 1 && coins.isEmpty => 0
           case _ => countChange(money, coins.tail) + countChange(money - coins.head, coins)

       }

  }

2

Đây là một thuật toán đệ quy đơn giản lấy một hóa đơn, sau đó đệ quy một hóa đơn nhỏ hơn cho đến khi nó đạt đến tổng, sau đó lấy một hóa đơn khác có cùng mệnh giá và lặp lại một lần nữa. Xem đầu ra mẫu dưới đây để minh họa.

var bills = new int[] { 100, 50, 20, 10, 5, 1 };

void PrintAllWaysToMakeChange(int sumSoFar, int minBill, string changeSoFar)
{
    for (int i = minBill; i < bills.Length; i++)
    {
        var change = changeSoFar;
        var sum = sumSoFar;

        while (sum > 0)
        {
            if (!string.IsNullOrEmpty(change)) change += " + ";
            change += bills[i];

            sum -= bills[i]; 
            if (sum > 0)
            {
                PrintAllWaysToMakeChange(sum, i + 1, change);
            }
        }

        if (sum == 0)
        {
            Console.WriteLine(change);
        }
    }
}

PrintAllWaysToMakeChange(15, 0, "");

In những thứ sau:

10 + 5
10 + 1 + 1 + 1 + 1 + 1
5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
5 + 5 + 1 + 1 + 1 + 1 + 1
5 + 5 + 5
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1

1

Duh, tôi cảm thấy mình thật ngu ngốc. Dưới đây là một giải pháp quá phức tạp, tôi sẽ giữ nguyên vì xét cho cùng thì nó cũng là một giải pháp. Một giải pháp đơn giản sẽ là:

// Generate a pretty string
val coinNames = List(("quarter", "quarters"), 
                     ("dime", "dimes"), 
                     ("nickel", "nickels"), 
                     ("penny", "pennies"))
def coinsString = 
  Function.tupled((quarters: Int, dimes: Int, nickels:Int, pennies: Int) => (
    List(quarters, dimes, nickels, pennies) 
    zip coinNames // join with names
    map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number
    map (t => t._1 + " " + t._2) // qty name
    mkString " "
  ))

def allCombinations(amount: Int) = 
 (for{quarters <- 0 to (amount / 25)
      dimes <- 0 to ((amount - 25*quarters) / 10)
      nickels <- 0 to ((amount - 25*quarters - 10*dimes) / 5)
  } yield (quarters, dimes, nickels, amount - 25*quarters - 10*dimes - 5*nickels)
 ) map coinsString mkString "\n"

Đây là giải pháp khác. Giải pháp này dựa trên quan sát rằng mỗi đồng xu là bội số của những đồng tiền khác, vì vậy chúng có thể được đại diện cho chúng.

// Just to make things a bit more readable, as these routines will access
// arrays a lot
val coinValues = List(25, 10, 5, 1)
val coinNames = List(("quarter", "quarters"), 
                     ("dime", "dimes"), 
                     ("nickel", "nickels"), 
                     ("penny", "pennies"))
val List(quarter, dime, nickel, penny) = coinValues.indices.toList


// Find the combination that uses the least amount of coins
def leastCoins(amount: Int): Array[Int] =
  ((List(amount) /: coinValues) {(list, coinValue) =>
    val currentAmount = list.head
    val numberOfCoins = currentAmount / coinValue
    val remainingAmount = currentAmount % coinValue
    remainingAmount :: numberOfCoins :: list.tail
  }).tail.reverse.toArray

// Helper function. Adjust a certain amount of coins by
// adding or subtracting coins of each type; this could
// be made to receive a list of adjustments, but for so
// few types of coins, it's not worth it.
def adjust(base: Array[Int], 
           quarters: Int, 
           dimes: Int, 
           nickels: Int, 
           pennies: Int): Array[Int] =
  Array(base(quarter) + quarters, 
        base(dime) + dimes, 
        base(nickel) + nickels, 
        base(penny) + pennies)

// We decrease the amount of quarters by one this way
def decreaseQuarter(base: Array[Int]): Array[Int] =
  adjust(base, -1, +2, +1, 0)

// Dimes are decreased this way
def decreaseDime(base: Array[Int]): Array[Int] =
  adjust(base, 0, -1, +2, 0)

// And here is how we decrease Nickels
def decreaseNickel(base: Array[Int]): Array[Int] =
  adjust(base, 0, 0, -1, +5)

// This will help us find the proper decrease function
val decrease = Map(quarter -> decreaseQuarter _,
                   dime -> decreaseDime _,
                   nickel -> decreaseNickel _)

// Given a base amount of coins of each type, and the type of coin,
// we'll produce a list of coin amounts for each quantity of that particular
// coin type, up to the "base" amount
def coinSpan(base: Array[Int], whichCoin: Int) = 
  (List(base) /: (0 until base(whichCoin)).toList) { (list, _) =>
    decrease(whichCoin)(list.head) :: list
  }

// Generate a pretty string
def coinsString(base: Array[Int]) = (
  base 
  zip coinNames // join with names
  map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number
  map (t => t._1 + " " + t._2)
  mkString " "
)

// So, get a base amount, compute a list for all quarters variations of that base,
// then, for each combination, compute all variations of dimes, and then repeat
// for all variations of nickels.
def allCombinations(amount: Int) = {
  val base = leastCoins(amount)
  val allQuarters = coinSpan(base, quarter)
  val allDimes = allQuarters flatMap (base => coinSpan(base, dime))
  val allNickels = allDimes flatMap (base => coinSpan(base, nickel))
  allNickels map coinsString mkString "\n"
}

Vì vậy, với 37 xu, ví dụ:

scala> println(allCombinations(37))
0 quarter 0 dimes 0 nickels 37 pennies
0 quarter 0 dimes 1 nickel 32 pennies
0 quarter 0 dimes 2 nickels 27 pennies
0 quarter 0 dimes 3 nickels 22 pennies
0 quarter 0 dimes 4 nickels 17 pennies
0 quarter 0 dimes 5 nickels 12 pennies
0 quarter 0 dimes 6 nickels 7 pennies
0 quarter 0 dimes 7 nickels 2 pennies
0 quarter 1 dime 0 nickels 27 pennies
0 quarter 1 dime 1 nickel 22 pennies
0 quarter 1 dime 2 nickels 17 pennies
0 quarter 1 dime 3 nickels 12 pennies
0 quarter 1 dime 4 nickels 7 pennies
0 quarter 1 dime 5 nickels 2 pennies
0 quarter 2 dimes 0 nickels 17 pennies
0 quarter 2 dimes 1 nickel 12 pennies
0 quarter 2 dimes 2 nickels 7 pennies
0 quarter 2 dimes 3 nickels 2 pennies
0 quarter 3 dimes 0 nickels 7 pennies
0 quarter 3 dimes 1 nickel 2 pennies
1 quarter 0 dimes 0 nickels 12 pennies
1 quarter 0 dimes 1 nickel 7 pennies
1 quarter 0 dimes 2 nickels 2 pennies
1 quarter 1 dime 0 nickels 2 pennies

1

Mục nhập blog này của tôi giải quyết vấn đề tương tự như thế này đối với các số liệu từ truyện tranh XKCD . Một thay đổi đơn giản đối với itemsdict vàexactcost giá trị cũng sẽ mang lại tất cả các giải pháp cho vấn đề của bạn.

Nếu vấn đề là tìm ra sự thay đổi sử dụng ít chi phí nhất, thì một thuật toán tham lam ngây thơ sử dụng nhiều đồng tiền có giá trị cao nhất cũng có thể thất bại đối với một số sự kết hợp giữa đồng tiền và số tiền mục tiêu. Ví dụ, nếu có các đồng tiền có giá trị 1, 3 và 4; và số tiền mục tiêu là 6 thì thuật toán tham lam có thể đề xuất ba đồng có giá trị 4, 1 và 1 khi dễ dàng thấy rằng bạn có thể sử dụng hai đồng mỗi đồng có giá trị 3.

  • Thóc.

1
public class Coins {

static int ac = 421;
static int bc = 311;
static int cc = 11;

static int target = 4000;

public static void main(String[] args) {


    method2();
}

  public static void method2(){
    //running time n^2

    int da = target/ac;
    int db = target/bc;     

    for(int i=0;i<=da;i++){         
        for(int j=0;j<=db;j++){             
            int rem = target-(i*ac+j*bc);               
            if(rem < 0){                    
                break;                  
            }else{                  
                if(rem%cc==0){                  
                    System.out.format("\n%d, %d, %d ---- %d + %d + %d = %d \n", i, j, rem/cc, i*ac, j*bc, (rem/cc)*cc, target);                     
                }                   
            }                   
        }           
    }       
}
 }

1

Tôi đã tìm thấy đoạn mã gọn gàng này trong cuốn sách "Python để phân tích dữ liệu" của O’reily. Nó sử dụng triển khai lười biếng và so sánh int và tôi cho rằng nó có thể được sửa đổi cho các mệnh giá khác bằng cách sử dụng số thập phân. Hãy cho tôi biết nó hoạt động như thế nào cho bạn!

def make_change(amount, coins=[1, 5, 10, 25], hand=None):
 hand = [] if hand is None else hand
 if amount == 0:
 yield hand
 for coin in coins:
 # ensures we don't give too much change, and combinations are unique
 if coin > amount or (len(hand) > 0 and hand[-1] < coin):
 continue
 for result in make_change(amount - coin, coins=coins,
 hand=hand + [coin]):
 yield result


1

Đây là cải tiến câu trả lời của Zihan. Rất nhiều vòng lặp không cần thiết xuất hiện khi mệnh giá chỉ là 1 xu.

Nó trực quan và không đệ quy.

    public static int Ways2PayNCents(int n)
    {
        int numberOfWays=0;
        int cent, nickel, dime, quarter;
        for (quarter = 0; quarter <= n/25; quarter++)
        {
            for (dime = 0; dime <= n/10; dime++)
            {
                for (nickel = 0; nickel <= n/5; nickel++)
                {
                    cent = n - (quarter * 25 + dime * 10 + nickel * 5);
                    if (cent >= 0)
                    {
                        numberOfWays += 1;
                        Console.WriteLine("{0},{1},{2},{3}", quarter, dime, nickel, cent);
                    }                   
                }
            }
        }
        return numberOfWays;            
    }

u không thể khái quát giải pháp này, do đó ví dụ như một yếu tố mới đi lên trong trường hợp đó bạn phải thêm một vòng lặp for
Sumit Kumar Saha

1

Giải pháp java đơn giản:

public static void main(String[] args) 
{    
    int[] denoms = {4,2,3,1};
    int[] vals = new int[denoms.length];
    int target = 6;
    printCombinations(0, denoms, target, vals);
}


public static void printCombinations(int index, int[] denom,int target, int[] vals)
{
  if(target==0)
  {
    System.out.println(Arrays.toString(vals));
    return;
  }
  if(index == denom.length) return;   
  int currDenom = denom[index];
  for(int i = 0; i*currDenom <= target;i++)
  {
    vals[index] = i;
    printCombinations(index+1, denom, target - i*currDenom, vals);
    vals[index] = 0;
  }
}

1
/*
* make a list of all distinct sets of coins of from the set of coins to
* sum up to the given target amount.
* Here the input set of coins is assumed yo be {1, 2, 4}, this set MUST
* have the coins sorted in ascending order.
* Outline of the algorithm:
* 
* Keep track of what the current coin is, say ccn; current number of coins
* in the partial solution, say k; current sum, say sum, obtained by adding
* ccn; sum sofar, say accsum:
*  1) Use ccn as long as it can be added without exceeding the target
*     a) if current sum equals target, add cc to solution coin set, increase
*     coin coin in the solution by 1, and print it and return
*     b) if current sum exceeds target, ccn can't be in the solution, so
*        return
*     c) if neither of the above, add current coin to partial solution,
*        increase k by 1 (number of coins in partial solution), and recuse
*  2) When current denomination can no longer be used, start using the
*     next higher denomination coins, just like in (1)
*  3) When all denominations have been used, we are done
*/

#include <iostream>
#include <cstdlib>

using namespace std;

// int num_calls = 0;
// int num_ways = 0;

void print(const int coins[], int n);

void combine_coins(
                   const int denoms[], // coins sorted in ascending order
                   int n,              // number of denominations
                   int target,         // target sum
                   int accsum,         // accumulated sum
                   int coins[],        // solution set, MUST equal
                                       // target / lowest denom coin
                   int k               // number of coins in coins[]
                  )
{

    int  ccn;   // current coin
    int  sum;   // current sum

    // ++num_calls;

    for (int i = 0; i < n; ++i) {
        /*
         * skip coins of lesser denomination: This is to be efficient
         * and also avoid generating duplicate sequences. What we need
         * is combinations and without this check we will generate
         * permutations.
         */
        if (k > 0 && denoms[i] < coins[k - 1])
            continue;   // skip coins of lesser denomination

        ccn = denoms[i];

        if ((sum = accsum + ccn) > target)
            return;     // no point trying higher denominations now


        if (sum == target) {
            // found yet another solution
            coins[k] = ccn;
            print(coins, k + 1);
            // ++num_ways;
            return;
        }

        coins[k] = ccn;
        combine_coins(denoms, n, target, sum, coins, k + 1);
    }
}

void print(const int coins[], int n)
{
    int s = 0;
    for (int i = 0; i < n; ++i) {
        cout << coins[i] << " ";
        s += coins[i];
    }
    cout << "\t = \t" << s << "\n";

}

int main(int argc, const char *argv[])
{

    int denoms[] = {1, 2, 4};
    int dsize = sizeof(denoms) / sizeof(denoms[0]);
    int target;

    if (argv[1])
        target = atoi(argv[1]);
    else
        target = 8;

    int *coins = new int[target];


    combine_coins(denoms, dsize, target, 0, coins, 0);

    // cout << "num calls = " << num_calls << ", num ways = " << num_ways << "\n";

    return 0;
}

1

Đây là một hàm C #:

    public static void change(int money, List<int> coins, List<int> combination)
    {
        if(money < 0 || coins.Count == 0) return;
        if (money == 0)
        {
            Console.WriteLine((String.Join("; ", combination)));
            return;
        }

        List<int> copy = new List<int>(coins);
        copy.RemoveAt(0);
        change(money, copy, combination);

        combination = new List<int>(combination) { coins[0] };
        change(money - coins[0], coins, new List<int>(combination));
    }

Sử dụng nó như thế này:

change(100, new List<int>() {5, 10, 25}, new List<int>());

Nó in:

25; 25; 25; 25
10; 10; 10; 10; 10; 25; 25
10; 10; 10; 10; 10; 10; 10; 10; 10; 10
5; 10; 10; 25; 25; 25
5; 10; 10; 10; 10; 10; 10; 10; 25
5; 5; 10; 10; 10; 10; 25; 25
5; 5; 10; 10; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 10; 25; 25; 25
5; 5; 5; 10; 10; 10; 10; 10; 10; 25
5; 5; 5; 5; 10; 10; 10; 25; 25
5; 5; 5; 5; 10; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 25; 25; 25
5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 10; 10; 25; 25
5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 10; 25; 25
5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 25; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5

Đầu ra khá đẹp
Cảm ơn bạn

1

Dưới đây là một chương trình python để tìm tất cả các tổ hợp tiền. Đây là một giải pháp lập trình động với thứ tự (n) thời gian. Tiền là 1,5,10,25

Chúng tôi chuyển từ tiền hàng 1 sang tiền hàng 25 (4 hàng). Hàng tiền 1 chứa số đếm nếu chúng ta chỉ xem tiền 1 khi tính số lượng kết hợp. Tiền hàng 5 tạo ra mỗi cột bằng cách lấy số tiền ở hàng r cho cùng số tiền cuối cùng cộng với số tiền 5 trước đó trong hàng của chính nó (vị trí hiện tại trừ đi 5). Tiền hàng 10 sử dụng tiền hàng 5, chứa số đếm cho cả 1,5 và thêm vào số tiền 10 trước đó (vị trí hiện tại trừ đi 10). Tiền hàng 25 sử dụng tiền hàng 10, chứa số lượng tiền hàng 1,5,10 cộng với số tiền 25 trước đó.

Ví dụ, số [1] [12] = số [0] [12] + số [1] [7] (7 = 12-5) dẫn đến 3 = 1 + 2; số [3] [12] = số [2] [12] + số [3] [9] (-13 = 12-25) dẫn đến 4 = 0 + 4, vì -13 nhỏ hơn 0.

def cntMoney(num):
    mSz = len(money)
    numbers = [[0]*(1+num) for _ in range(mSz)]
    for mI in range(mSz): numbers[mI][0] = 1
    for mI,m in enumerate(money):
        for i in range(1,num+1):
            numbers[mI][i] = numbers[mI][i-m] if i >= m else 0
            if mI != 0: numbers[mI][i] += numbers[mI-1][i]
        print('m,numbers',m,numbers[mI])
    return numbers[mSz-1][num]

money = [1,5,10,25]
    num = 12
    print('money,combinations',num,cntMoney(num))

output:    
('m,numbers', 1, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
('m,numbers', 5, [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3])
('m,numbers', 10, [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4])
('m,numbers', 25, [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4])
('money,combinations', 12, 4)

0

Giải pháp Java

import java.util.Arrays;
import java.util.Scanner;


public class nCents {



public static void main(String[] args) {

    Scanner input=new Scanner(System.in);
    int cents=input.nextInt();
    int num_ways [][] =new int [5][cents+1];

    //putting in zeroes to offset
    int getCents[]={0 , 0 , 5 , 10 , 25};
    Arrays.fill(num_ways[0], 0);
    Arrays.fill(num_ways[1], 1);

    int current_cent=0;
    for(int i=2;i<num_ways.length;i++){

        current_cent=getCents[i];

        for(int j=1;j<num_ways[0].length;j++){
            if(j-current_cent>=0){
                if(j-current_cent==0){
                    num_ways[i][j]=num_ways[i-1][j]+1;
                }else{
                    num_ways[i][j]=num_ways[i][j-current_cent]+num_ways[i-1][j];
                }
            }else{
                num_ways[i][j]=num_ways[i-1][j];
            }


        }


    }



    System.out.println(num_ways[num_ways.length-1][num_ways[0].length-1]);

}

}


0

Giải pháp java dưới đây cũng sẽ in các kết hợp khác nhau. Dễ hiểu. Ý tưởng là

cho tổng 5

Giải pháp là

    5 - 5(i) times 1 = 0
        if(sum = 0)
           print i times 1
    5 - 4(i) times 1 = 1
    5 - 3 times 1 = 2
        2 -  1(j) times 2 = 0
           if(sum = 0)
              print i times 1 and j times 2
    and so on......

Nếu tổng còn lại trong mỗi vòng lặp nhỏ hơn mệnh giá tức là nếu tổng còn lại 1 nhỏ hơn 2, thì chỉ cần ngắt vòng lặp

Mã hoàn chỉnh bên dưới

Xin vui lòng sửa cho tôi trong trường hợp có bất kỳ sai lầm

public class CoinCombinbationSimple {
public static void main(String[] args) {
    int sum = 100000;
    printCombination(sum);
}

static void printCombination(int sum) {
    for (int i = sum; i >= 0; i--) {
        int sumCopy1 = sum - i * 1;
        if (sumCopy1 == 0) {
            System.out.println(i + " 1 coins");
        }
        for (int j = sumCopy1 / 2; j >= 0; j--) {
            int sumCopy2 = sumCopy1;
            if (sumCopy2 < 2) {
                break;
            }
            sumCopy2 = sumCopy1 - 2 * j;
            if (sumCopy2 == 0) {
                System.out.println(i + " 1 coins " + j + " 2 coins ");
            }
            for (int k = sumCopy2 / 5; k >= 0; k--) {
                int sumCopy3 = sumCopy2;
                if (sumCopy2 < 5) {
                    break;
                }
                sumCopy3 = sumCopy2 - 5 * k;
                if (sumCopy3 == 0) {
                    System.out.println(i + " 1 coins " + j + " 2 coins "
                            + k + " 5 coins");
                }
            }
        }
    }
}

}


0

Đây là một giải pháp dựa trên python sử dụng đệ quy cũng như ghi nhớ dẫn đến độ phức tạp của O (mxn)

    def get_combinations_dynamic(self, amount, coins, memo):
    end_index = len(coins) - 1
    memo_key = str(amount)+'->'+str(coins)
    if memo_key in memo:
        return memo[memo_key]
    remaining_amount = amount
    if amount < 0:
        return []
    if amount == 0:
        return [[]]
    combinations = []
    if len(coins) <= 1:
        if amount % coins[0] == 0:
            combination = []
            for i in range(amount // coins[0]):
                combination.append(coins[0])
            list.sort(combination)
            if combination not in combinations:
                combinations.append(combination)
    else:
        k = 0
        while remaining_amount >= 0:
            sub_combinations = self.get_combinations_dynamic(remaining_amount, coins[:end_index], memo)
            for combination in sub_combinations:
                temp = combination[:]
                for i in range(k):
                    temp.append(coins[end_index])
                list.sort(temp)
                if temp not in combinations:
                    combinations.append(temp)
            k += 1
            remaining_amount -= coins[end_index]
    memo[memo_key] = combinations
    return combinations

Được rồi, tôi nghi ngờ ở trên có thời gian chạy đa thức. Không chắc liệu chúng ta có thể có thời gian chạy đa thức hay không. Nhưng những gì tôi đã quan sát là ở trên chạy nhanh hơn so với phiên bản không ghi nhớ trong nhiều trường hợp. Tôi sẽ tiếp tục nghiên cứu tại sao
lalatnayak
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.