Iconミニ講座3(正数の分割)
今回は、正数の部分和への分解です。これは、5文字の文字列があるとして、
例えば、3文字と2文字の2つの単語として、辞書を参照するためのものです。
5の分解でも、5(分解しない)、4+1、3+2、3+1+1、...と
いって、最後は1+1+1+1+1と、かなりのパターンが発生します。
こういう面倒な繰り返しが必要な処理は、再帰処理を行うとプログラムが楽に
なることが多いみたいですね。
処理は、5の例ですと、5から順次 5、4、3、2、1と引き算していく
やり方にしています。
最初は、5から5を引くケースで、
余りは0です。 結果は[5]を返します。
次は、5から4を引くケースで、
余りは1です。余りがある場合は、中間結果として[4]を保持して、
自分自身を更に呼んで(再帰)、余りの1を与えて、結果をもらいます。
その結果として[1]が返って来ます。中間結果の[4]に[1]を追加して
[4,1]を結果として返します。
次は、5から3を引くケースで、
1回目の動作 中間結果1 [3] 余り 2 ・・・余り処理のため再帰
2回目の動作 中間結果1 [2] 余り 0
2 [1] 余り 1 ・・・余り処理のため再帰
3回目の動作 結果1 [1]
という動作を経ますので、結果は[3,2]と、[3,1,1]の2つが返り
ます。
こんな風な動作となります。
-----^ N_DIV.ICN ( date:03-08-20 time:19:43 ) --------------<cut here
####################
# 正数分割
####################
# n_div.icn Rev.1.0 2003/08/20 windy 風つかい H.S.
####################
# Usage n_div 正数
# 与えられた数字を部分和に分解する。
# 例:5->[5],[4,1],[3,2],[3,1,1],[2,2,1],[2,1,1,1],[1,1,1,1,1]
# This file is in the public domain.
procedure main(args)
Usage := "n_div 正数"
if *args < 1 then stop(Usage) # コマンドライン引数が無ければ Usage表示
if args[1] < 1 then stop(Usage) # 正数でなければ Usage表示
# ↓正数分割 generatorから順次結果を取り出し
every L := n_div(args[1]) do show_sl(L)
# ↑listの内容を表示する
end
####################
# 正数分割 generator
####################
# arg [1]: 分割される数(正数)(再帰の場合は、余り)
# value : 分解結果の数 listに格納
# Usage : every L := n_div(r) do ...
procedure n_div(r)
if r < 1 then fail # 念のため
if r = 1 then return [1] # 再帰終了
# r >= 2 の場合
every i := r to 1 by -1 do { # r から順に −1しながら
rr := r -i # 元の数から引き算していく
if rr = 0 then suspend [i] # 余りが 0なら結果を返す。
else suspend [i] ||| n_div(rr)
# ↑余りがでたら、再帰してその結果を listの末尾
# に追加。 ||| は listの要素の追加演算子
} # suspend は、複数の結果を返す return
end
# 以下は、Icon入門講座より
####################
# listの 内容表示
####################
# arg : list
# value : null
# Usage : show_sl(L)
# 最終的に stringか numberが要素であること
# Icon入門講座(13),Icon入門講座3(15)
procedure show_sl(list)
# listの内容表示(test/表示用)
every writes(" ",!list)
write()
return
end
-----$ N_DIV.ICN ( lines:57 words:192 ) --------------------<cut here
n_div 5 >ccc としますと、こんな結果になります。
-----^ CCC ( date:03-08-20 time:19:44 ) --------------------<cut here
5
4 1
3 2
3 1 1
2 3
2 2 1
2 1 2
2 1 1 1
1 4
1 3 1
1 2 2
1 2 1 1
1 1 3
1 1 2 1
1 1 1 2
1 1 1 1 1
-----$ CCC ( lines:16 words:48 ) ---------------------------<cut here
以上の結果を良く見ますと、5=4+1と1+4や 3+2と2+3の両方が
現れています。 辞書検索は、どちらか一方を行えば、残りは入れ替えるだけで
人が判断できると思います。 ということで、片方だけにするために、結果は、
降順しか許さないという条件を付けましょう。 ということで、再帰の際に制限
を付けました。
-----^ N_DIVD.ICN ( date:03-08-20 time:19:42 ) -------------<cut here
####################
# 正数分割
####################
# n_divd.icn Rev.1.0 2003/08/20 windy 風つかい H.S.
####################
# Usage n_divd 正数
# 与えられた数字を部分和に分解する。結果は降順の分解のみ。
# 例:5->[5],[4,1],[3,2],[3,1,1],[2,2,1],[2,1,1,1],[1,1,1,1,1]
# This file is in the public domain.
procedure main(args)
Usage := "n_divd 正数"
if *args < 1 then stop(Usage) # コマンドライン引数が無ければ Usage表示
if args[1] < 1 then stop(Usage) # 正数でなければ Usage表示
# ↓正数分割 generatorから順次結果を取り出し
every L := n_divd(args[1]) do show_sl(L)
# ↑listの内容を表示する
end
####################
# 正数分割 generator
####################
# 5=3+2 等に分割する。分割は降順のみ許す。(例 5=2+3は除外)
# arg [1]: 分割される元の数(再帰の場合は、前の処理の余り)
# [2]: 最大数 (5=2+2+1 の場合に、最初の 2の時 3が余るが、再帰して 3の
# 分割を始める時、3からではなく 2から始めるための細工。降順手当。)
# value : 分解結果の数。listに格納
# Usage : every L := n_divd(r) do ...
procedure n_divd(r,max)
/max := r # 指定無きは、分割される元の数そのもの
if r < 1 then fail # 念のため
if r = 1 then return [1] # 再帰終了
# r >= 2 の場合
if r > max then rs := max # 分割(取り去る)数の最大数の設定。
else rs := r # 分割される数か 最大数指定の 小さい方
every i := rs to 1 by -1 do { # 上記指定数〜1迄、順に−1しながら
rr := r -i # 新たな余り
if rr = 0 then suspend [i] # 余りが無ければ
# 余りがあれば、再帰処理
else if rr > i then suspend [i] ||| n_divd(rr,i) # 余りが大き過ぎる時
else suspend [i] ||| n_divd(rr)
}
end
# 以下は、Icon入門講座より
####################
# listの 内容表示
####################
# arg : list
# value : null
# Usage : show_sl(L)
# 最終的に stringか numberが要素であること
# Icon入門講座(13),Icon入門講座3(15)
procedure show_sl(list)
# listの内容表示(test/表示用)
every writes(" ",!list)
write()
return
end
-----$ N_DIVD.ICN ( lines:63 words:231 ) -------------------<cut here
n_divd 5 >ddd とすると結果は、こうなります。 だいぶパターンが減り
ました。 それでも、未だ随分ありますので、分割数や最小数に制限をつけたい
と思います。それは、次回に。
-----^ DDD ( date:03-08-20 time:19:45 ) --------------------<cut here
5
4 1
3 2
3 1 1
2 2 1
2 1 1 1
1 1 1 1 1
-----$ DDD ( lines:7 words:20 ) ----------------------------<cut here
風つかい(hshinoh@...)
IconのWWWは、 http://www.cs.arizona.edu/icon/
UniconのWWWは、http://unicon.sourceforge.net/index.html
BGM: Battery's not included / 森山威男&杉本喜代志