分割数の表示

数学ガールを読んだら分割数というものがのっていたのでそれを表示するためのプログラム。
まずはじめに作ったのがこれです。

#!/usr/bin/env perl
use strict;
use warnings;

sub min{
	my($x,$y) = (@_);
	if($x > $y){
		return $y;
	}else{
		return $x;
	}
}

my $count = 0;
my $space = 0; #スペース(タブ)を出力したかどうか

sub pertitionNum{
	my($i,$n,$sum,$num) = (@_);
	#$n:今までに出力した数字の数 $sum:今までの合計
	if($sum >= $num){
		$count++;
		$space = 0;
		print "\n"; 
		return;
	}

	for(;$i > 0;$i--){
		next if($sum + $i > $num);
		if($space == 0){
			print "\t" x (2*$n);
			$space = 1;
		}
		print $sum == 0 ? "\r\t=\t" : "\t+\t";
		print $i;
		&pertitionNum(min($num-$i,$i),$n+1,$sum + $i,$num);
	}
}

print "num: ";
chomp(my $num = <STDIN>);
print "\n$num";
&pertitionNum($num,0,0,$num);
print "\ncount: $count\n";


再帰で余裕でしょって思いましたが表示にてこずりました。とりあえず\rを使うことで解決。min()は高速化のためです(ない方がはやかったりして)。これを実行するとこんな感じです。

5 = 5
  = 4 + 1
  = 3 + 2
  =   + 1 + 1
  = 2 + 2 + 1
  = 2 + 1 + 1 + 1
  = 1 + 1 + 1 + 1 + 1

count: 7

再帰で実行しているので当たり前ですが、4行目は完全に表示させることができません。それに同じ数字の繰り返しをn*iみたいに表すこともできません。ということで配列を使うように書き直しました(python勉強中なのでpythonで)。

#!/usr/bin/env python

def printnum(x):
    plus = False
    print "\t",
    for i in xrange(len(x)-1,0,-1):
        if x[i] > 0:
            if plus: print "+",
            else: plus = True

            if x[i] > 1: print str(i) + "x" + str(x[i]),
            else: print i,
    print 


def partitionNum(i,sum,num):
    global count
    if sum >= num:
        count += 1
        printnum(x)
        return

    for i in xrange(i,0,-1):
        if sum + i > num : continue 
        x[i] += 1
        dividednum(i,sum+i,num)
        x[i] -= 1


if __name__ == "__main__":
    count = 0
    num = int(raw_input('num: '))
    x = [0]*(num+1)
    if num > 0 :print num,"=",
    partitionNum(num,0,num)
    print "count:",count

これを実行するとこんな感じです。

5 = 5
  = 4 + 1
  = 3 + 2
  = 3 + 1x2
  = 2x2 + 1
  = 2 + 1x3
  = 1x5
count: 7

min()はいらないような気がしたので削除。こっちの方がさっきより全然いいですね。


「考えて分からなかったら手を使ってプログラムで解くしかないでしょ」