How to solve the Josephus problem in SQL
December 16th, 2007 By Frank Zhou
The following is an interesting problem posted by Jsoftware:
With n people numbered 1 to n in a circle, every second one is eliminated until only one survives. Determine the elimination order and the survivor’s number.
—————————————–SQL Solution——————-
variable input_num number
exec :input_num := 10;
set timing on
SELECT trim(BOTH ','FROM regexp_replace(XMLAgg(XMLElement(X,
CASE WHEN iteration IS NOT NULL THEN data END)
ORDER BY rank),'<X>|</X><X>|</X>',',')) AS elimination_order,
max(CASE WHEN iteration IS NULL THEN data END)
KEEP (DENSE_RANK LAST ORDER BY rank) survive_num
FROM
(SELECT row_number() OVER (ORDER BY iteration NULLS LAST, rn) rank,
data, iteration
FROM
(SELECT rn, data, iteration
FROM
(SELECT num, rn FROM
(SELECT LEVEL num, LEVEL rn FROM dual CONNECT BY LEVEL <=:input_num)
)
MODEL
DIMENSION BY (rn)
MEASURES
(num, num new_rank, CAST(NULL AS NUMBER) old_num,
CAST(NULL AS VARCHAR2(1)) flag, num data,
CAST(NULL AS NUMBER) iteration, CAST(NULL AS NUMBER) cnt
)
RULES ITERATE(1000) UNTIL (cnt[1] = 1)
( flag[ANY] = CASE iteration_number
WHEN 0 THEN 'T'
ELSE flag[CV()] END,
new_rank[ANY] = CASE WHEN flag[cv()] = 'T'
THEN CASE WHEN mod(new_rank[cv()],2) = 1
THEN ceil(new_rank[cv()]/2) END
ELSE CASE WHEN mod(new_rank[cv()],2) = 0
THEN ceil(new_rank[cv()]/2) END
END,
old_num[ANY] = CASE WHEN new_rank[cv()] IS NULL AND
num[cv()] IS NOT NULL
THEN num[cv()] END,
num[ANY] = CASE WHEN new_rank[cv()] IS NOT NULL
THEN num[cv()] END,
flag[ANY] = CASE WHEN max(old_num)[ANY] > max(num)[ANY]
THEN 'T'
ELSE 'F' END,
cnt[1] = count(num)[ANY],
iteration[ANY] = CASE WHEN old_num[cv()] IS NOT NULL
THEN iteration_number
ELSE iteration[cv()]
END
)
)
);
ELIMINATION_ORDER SURVIVE_NUM
-------------------------- ------------
2,4,6,8,10,3,7,1,9 5
Elapsed: 00:00:00.31

June 25th, 2010 at 8:29 am
11GR2: WITH t (cnt,people,pos,eliminated) AS ( SELECT MAX(ROWNUM) cnt,MAX(SYS_CONNECT_BY_PATH(LPAD(ROWNUM,4),',')),6,'' FROM DUAL CONNECT BY ROWNUM<=:input_num UNION ALL SELECT cnt-1,SUBSTR(people,1,pos-1)||SUBSTR(people,pos+5) ,CASE WHEN pos+5<LENGTH(SUBSTR(people,1,pos-1)||SUBSTR(people,pos+5)) THEN pos+5 WHEN pos+5<LENGTH(people) THEN 1 ELSE 6 END ,CAST(eliminated||SUBSTR(people,pos,5) AS VARCHAR2(200)) FROM t WHERE cnt>1 ) SELECT people,eliminated FROM t WHERE cnt=1; PEOPLE ------------------------------------------------------- ELIMINATED ------------------------------------------------------- , 5 , 2, 4, 6, 8, 10, 3, 7, 1, 9