OraQA

Oracle Question and Answer

  • Do you have a solution to a problem? Do you have an unanswered question? Login and share it with the Oracle community. More...

Oracle News


Entries RSS feed

Comments RSS feed

How to solve the 1000 $1 Bills in 10 Envelopes Puzzle in SQL

October 12th, 2008 By Frank Zhou

The following is an interesting problem posted by mathforum.org:

You are given 1000 one dollar bills and 10 envelopes. Put the bills
into the envelopes in such a way that someone can ask you for any
amount of money from $1 to $1000 (examples $532, $619, $88, etc.)
and you can give it to them through a combination of the envelopes.

The following SQL pattern can be used to solve this puzzle, it also proves
that the result is correct.

————————————————–10G SQL Solution————————————————

SELECT   ROW_NUMBER () OVER (ORDER BY num) envelope_num,
         num dollars,
         MIN from_min,
         MAX to_max,
         cnt combo_cnt
    FROM (WITH DATA AS
               (SELECT num
                  FROM (SELECT CASE
                                  WHEN SUM (num) OVER (ORDER BY num) > 1000
                                     THEN   num
                                          - (  SUM (num) OVER (ORDER BY num)
                                             - 1000
                                            )
                                  ELSE num
                               END AS num
                          FROM (SELECT     POWER (2, LEVEL - 1) AS num
                                      FROM DUAL
                                CONNECT BY POWER (2, LEVEL - 1) <= 1000))),
               data_check AS
               (SELECT MAX (num) MAX,
                       MIN (num) MIN,
                       COUNT (*) cnt
                  FROM (SELECT DISTINCT SUM
                                           (TO_NUMBER (REGEXP_SUBSTR (str,
                                                                      '[^,]+',
                                                                      1,
                                                                      LEVEL
                                                                     )
                                                      )
                                           ) num
                                   FROM (SELECT     SYS_CONNECT_BY_PATH (num,
                                                                         ','
                                                                        ) str
                                               FROM (SELECT num
                                                       FROM DATA)
                                         CONNECT BY PRIOR num < num)
                             CONNECT BY PRIOR str = str
                                    AND REGEXP_SUBSTR (str, '[^,]+', 1, LEVEL) IS NOT NULL
                                    AND PRIOR DBMS_RANDOM.STRING ('p', 20) IS NOT NULL
                               GROUP BY str))
          SELECT MAX,
                 MIN,
                 cnt,
                 num
            FROM data_check FULL OUTER JOIN DATA ON (data_check.MIN = DATA.num
                                                    )
                 )
ORDER BY num NULLS FIRST;

SQL> /

ENVELOPE_NUM    DOLLARS      FROM_MIN      TO_MAX       COMBO_CNT
------------   ----------   ----------   ----------    ----------
1              1                1         1000          1000
2              2
3              4
4              8
5             16
6             32
7             64
8            128
9            256
10            489

10 rows selected.

Elapsed: 00:00:00.43

One Response to “How to solve the 1000 $1 Bills in 10 Envelopes Puzzle in SQL”

  1. newkid Says:

    Your solution is brilliant. Here’s an easy way to verify it:

    WITH DATA AS (SELECT num,MAX(num) OVER() m
                    FROM (SELECT CASE
                                    WHEN SUM (num) OVER (ORDER BY num) > 1000
                                       THEN   num
                                            - (  SUM (num) OVER (ORDER BY num)
                                               - 1000
                                              )
                                    ELSE num
                                 END AS num
                            FROM (SELECT POWER (2, LEVEL - 1) AS num
                                   FROM DUAL
                                  CONNECT BY POWER (2, LEVEL - 1) <= 1000
                                  )
                          )
                   )
    ,TO_CHECK AS (SELECT ROWNUM n FROM DUAL CONNECT BY ROWNUM <=1000)
    SELECT n
      FROM (
    SELECT n,num
      FROM TO_CHECK, DATA
     WHERE (CASE WHEN n>m THEN n-m ELSE n END)>num-1 AND BITAND((CASE WHEN n>m THEN n-m ELSE n END),num)>0 AND num < m
            OR (n>m AND num=m)
     )
    GROUP BY n HAVING SUM(num) != n
    
    Or:
    
    WITH DATA AS (SELECT num,MAX(num) OVER() m
                    FROM (SELECT CASE
                                    WHEN SUM (num) OVER (ORDER BY num) > 1000
                                       THEN   num
                                            - (  SUM (num) OVER (ORDER BY num)
                                               - 1000
                                              )
                                    ELSE num
                                 END AS num
                            FROM (SELECT POWER (2, LEVEL - 1) AS num
                                   FROM DUAL
                                  CONNECT BY POWER (2, LEVEL - 1) < = 1000
                                  )
                          )
                   )
    ,TO_CHECK AS (SELECT ROWNUM n FROM DUAL CONNECT BY ROWNUM  <=1000)
    SELECT num FROM DATA
    WHERE NOT EXISTS (
    SELECT n
      FROM (
    SELECT n,num
      FROM TO_CHECK, DATA
     WHERE (CASE WHEN n>m THEN n-m ELSE n END)>num-1 AND BITAND((CASE WHEN n>m THEN n-m ELSE n END),num)>0 AND num < m
            OR (n>m AND num=m)
     )
    GROUP BY n HAVING SUM(num) != n
    )
    ;
    

Leave a Reply

You must be logged in to post a comment.

RSS feed for comments on this question