過去ログ 12
過去ログは下にいくほど新しい記事となっております。
※注意:このサイトにあるプログラムのソースコードや、その他の情報はすべて無保証です。やばいと思ったら実行しないでください。コンパイル、実行などによって生じた損害に対する責任は負いませんので、ご了承ください。
クリスマスイブ
24-12-2009
明日はクリスマス。
※今日はこれだけ
あけおめ
2-1-2010
すごろくしようと思ったけど、サイコロが見つからない。そんな時は・・・。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(){ srand(time(NULL)); printf("%d\n", rand() % 6 + 1); return 0; }
これで大丈夫。
・・・
・・
・
でも、毎回プログラムを起動するのは面倒なので、一気に出してしまいましょう。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(){ int i; srand(time(NULL)); for(i = 0; i < 999; i++){ printf("%d ", rand() % 6 + 1); } return 0; }
これで999回分のサイコロ結果が一気に表示されます。適当に改行してもいいんじゃない?
実行例↓
6 1 1 4 5 2 5 5 4 6 1 1 2 6 3 6 2 3 4 1 5 5 6 3 2 3 5 1 3 1 4 6 1 2 4 3 4 2 5 5 5 5 3 6 2 5 4 2 5 1 2 3 3 5 5 4 2 1 2 2 1 6 1 5 1 4 5 2 4 4 6 2 6 6 2 6 5 5 1 3 3 6 4 5 4 2 6 3 1 2 4 5 5 5 4 5 6 6 5 3 3 4 5 1 2 4 6 6 6 4 6 2 3 3 4 6 3 3 1 3 2 4 1 6 6 2 3 6 2 5 6 2 6 2 2 1 5 5 6 4 2 4 3 4 6 6 2 2 1 6 2 6 3 1 4 1 2 6 6 1 2 5 3 1 1 2 2 3 1 5 5 6 2 1 2 2 5 3 1 3 2 1 2 2 1 5 2 6 2 5 1 3 2 3 4 2 4 5 2 2 3 6 2 3 5 3 4 3 3 2 5 2 2 4 1 2 3 3 2 4 5 6 5 6 2 2 5 3 4 1 5 4 6 4 6 4 4 1 4 6 3 6 5 2 4 5 2 4 5 3 1 4 6 3 3 1 4 2 1 5 2 3 3 5 6 6 1 1 1 2 6 1 2 2 2 3 1 3 6 5 3 4 6 2 1 3 6 4 4 1 3 3 3 3 5 1 2 5 1 2 5 5 6 6 6 2 6 4 4 5 1 1 2 6 6 2 2 6 4 3 6 4 3 6 6 2 6 5 4 1 5 2 3 4 5 6 5 4 4 3 2 4 1 4 1 6 3 3 3 6 3 2 3 6 2 6 5 5 5 2 3 3 2 5 6 6 5 5 2 6 5 3 1 3 4 1 6 5 1 3 4 4 4 5 1 3 4 5 2 6 4 4 2 5 1 6 5 5 2 4 4 4 4 4 6 2 2 5 4 3 5 1 4 1 3 4 3 5 6 2 4 3 4 4 2 4 1 4 6 2 1 3 5 4 4 2 3 5 4 6 5 3 5 2 3 1 5 3 5 4 3 1 5 6 4 6 3 2 1 2 3 1 2 5 2 3 6 3 5 1 2 4 1 4 5 3 5 2 4 1 5 6 5 1 3 6 6 5 1 6 4 3 4 3 5 4 5 4 6 3 3 1 6 3 3 3 4 1 4 1 5 6 4 4 1 6 3 4 2 2 2 3 2 5 5 1 2 1 4 5 1 4 4 5 1 6 1 4 4 2 2 2 1 3 3 5 2 4 3 1 5 4 1 6 6 3 6 6 1 2 4 1 5 1 5 5 6 3 6 3 4 1 3 3 3 5 1 2 6 3 6 4 4 4 2 4 6 1 3 6 6 6 5 3 5 3 5 2 4 5 5 1 3 5 1 6 1 2 5 1 2 3 4 6 6 3 1 6 2 3 3 1 6 1 3 4 2 6 6 5 4 2 3 6 6 4 3 6 3 2 4 4 4 2 1 1 2 1 6 3 1 1 4 1 1 4 2 2 3 5 4 4 6 1 2 3 2 4 1 4 3 4 5 4 3 6 5 5 4 2 1 5 2 2 3 3 4 4 2 4 3 4 2 6 4 3 3 3 4 1 6 5 4 2 2 5 1 4 3 3 6 3 5 5 3 1 5 6 2 1 3 4 2 2 2 3 2 2 5 4 2 4 2 3 3 1 1 4 5 3 4 2 4 2 4 4 6 3 3 1 1 3 3 2 5 4 4 4 5 2 1 4 3 6 6 5 1 1 6 3 1 3 2 2 2 5 5 1 1 5 2 1 6 4 6 2 5 3 5 3 4 4 6 4 3 3 1 3 1 6 3 2 1 4 1 6 3 4 1 1 2 6 2 1 1 1 2 5 2 5 5 3 2 2 1 4 4 1 5 5 4 1 4 4 5 2 4 5 5 2 5 1 1 6 5 5 5 5 3 6 3 5 2 2 4 6 5 1 6 3 3 4 4 4 5 6 6 6 4 2 1 2 2 5 6 5 3 4 1 3 1 3 1 6 2 4 6 6 3 5 1 3 6 2 5 5 1 2 4 2 3 3 3 3 1 6 1 4 1 1 6 1 1 5 1 2 6 4 5 6 6 5 1 6 4 5 2 4 6 3 3 2 5 4 2 6 3 2 1 4 6 4 2 4 6 6 5 6 3 2 3 3 4 3 6 2 1 1 3 4 1 6 4 6 3 5 3 5 5 1 6 4 4 6 2 2 3 4 5 6 3 1 6 1 4 3 2 2 1 4 4 1 3 1 4 3 5 6 6
結果が既に分かっているのは面白くないか。
ところで、結果がなんか偏ってない?
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <math.h> #define LOOPNUM 999 int main(){ int i,j; double res[LOOPNUM]; double avr = 0; double sigma = 0; int count; srand(time(NULL)); for(i = 0; i < LOOPNUM; i++){ res[i] = rand() % 6 + 1; printf("%.0lf ", res[i]); avr += res[i]; } puts(""); puts(""); for(i = 1; i <= 6; i++){ count = 0; for(j = 0; j < LOOPNUM; j++){ if(res[j] == i){ count++; } } printf("%d : %d回\n", i, count); } puts(""); avr /= LOOPNUM; printf("平均 %lf\n",avr); for(i = 0; i < LOOPNUM; i++){ res[i] = (avr - res[i]) * (avr - res[i]); sigma += res[i]; } sigma /= LOOPNUM; printf("分散 %lf\n", sigma); sigma = sqrt(sigma); printf("標準偏差 %lf\n", sigma); return 0; }
実行結果例↓
(数字部分省略) 1 : 183回 2 : 156回 3 : 159回 4 : 178回 5 : 160回 6 : 163回 平均 3.465465 分散 2.959518 標準偏差 1.720325 (2回目) 1 : 155回 2 : 171回 3 : 180回 4 : 147回 5 : 193回 6 : 153回 平均 3.511512 分散 2.828446 標準偏差 1.681798 (3回目) 1 : 148回 2 : 153回 3 : 188回 4 : 182回 5 : 160回 6 : 168回 平均 3.557558 分散 2.771212 標準偏差 1.664696
そうでもないか。
※メール、リンクは適当に。
大学入試センター試験
17-1-2010
今日あったんだね。
このサイト的にはBASIC問題を取り上げるべきか。
BASICについて
BASICはCよりも簡単な言語です。たとえば、C言語のHello Worldは、
#include <stdio.h> int main(){ printf("Hello World\n&"); return 0; }
ですが、BASICではこうなります。
10 PRINT "Hello World"
これだけです。10は行番号です。関数の概念がないので、mainもいりません。文字出力のPRINTも標準装備です。
関数が作れず、構造化が難しいので、複雑な処理をさせようとするとC言語他ではタブーとされているgoto文を多用させることになります。
ただ、基本的な構文さえ知っていればすぐにプログラミングできるので、プログラミングの入門言語、または「数学」の問題にはもってこいの言語です。
なお、最新のVisual Basic .NETは関数もクラスも使えるオブジェクト指向言語ですので、気軽に高度なプログラムができる、初心者向け以上の言語となっています。
(LinuxでのVisual Basicプログラミングはmonoを使うとできます。)
21年度問題
試験問題の公開が明日になりそうなので、21年度の問題を見てみましょう。
p,qを異なる自然数とする。このとき,与えられた自然数dについて,d以下の自然数のうちで
k = mp + nq (m,nは0以上の整数) ……… (*)
のように表すことができるものを小さい順にすべて列挙し,最後にその個数を表示したい。そのために次のような〔プログラム〕を作った。ここで,INT(X)はXを超えない最大の整数を表す関数である。
〔プログラム〕
100 INPUT PROMPT "p=": P l10 INPUT PROMPT "q=": Q 120 INPUT PROMPT "d=": D 130 LET U=0 140 FOR K=1 TO D 150 IF K-INT(K/P)*P=O THEN [ ア ] 160 FOR M=O TO INT(K/P) 170 LET R=K-M*P 180 IF [ イ ] THEN [ ア ] 190 NEXT M 200 [ ウ ] 210 PRINT K 220 [ エ ] 230 NEXT K 240 PRINT "総数="; U 250 END
以下、選択肢が続きますが、選択肢をあえて見ずに考えてみると。
ポイントとなるところは、行番号150の「K-INT(K/P)*P=0」が何を意味しているか、というところでしょう。
これはKがPの倍数であるかどうかを調べている処理です。まず最初にK/Pが計算されます。K=5、P=2なら、計算結果は2.5ですが、整数型に直されますので、結果2。2*Pは2*2で4、K-4は5-4=1で、5は2の倍数でない、ということが分かります。K=6、P=2であれば、K/P=3、INT(K/P)も3、INT(K/P)*P=6でK-6=0となり、6は2の倍数である、ということになります。なお、MOD(ある数で割ったあまりを返す)を使えばもっとわかりやすくなりますが、試験問題としては、こちらの方がいいのでしょう。このプログラムは文字が密集していて読みにくいし。簡単に正解されてたまるか、という製作者の気持ちが込められています。
なお、このようにわざと文字を詰めてファイルサイズ自体を圧縮する手法はBASICのようなインタープリタ形式の言語ではかつてよくおこなわれていました。コンピュータが実行と同時にソースコードを解析するので、こうした方が、プログラムの実効速度が劇的に速くなるのです。注釈を書くと、やはり遅くなるので、書かなかったりしました。
さて、KがPの倍数であれば、K=MPとなるMは存在し、N=0のときにK=MP+NQが成立しますので、Kは要件を満たすことになります。なので、[ ア ]はKを出力している210かその1つ前の200に移動ということになります。
次に、[ イ ]ですが、満たすときに[ ア ]を行っているので、[ イ ]もK=MP+NQが成立する条件ということになります。
ここで、Rに注目しますと、RはK-M*P=NQなので、R=NQとなります。このときRがQの倍数(Nが整数)であれば成立するので、[ イ ]はR-INT(R/Q)*Q=0となります。まあ、普通に「R MOD Q = 0」の方が素直だとおもいますが。
次に[ ウ ]ですが、条件を満たした奴は[ ア ]で飛ばされるので、ここにくるのは条件を満たさなかった奴。BASICではNEXT文でインクリメントされますので、[ ウ ]はGOTO 230が正解。また、これにより、[ ア ]はGOTO 210に決まります。GOTO 200にすると、出力されずに次の数に行ってしまうので。
[ エ ]は数のカウントをしなければいけないので、LET U=U+1になります。なお、BASICではLETは省略できるので、書かないことも多いです。
これで穴埋め問題は終了ですね。
これ以降の問題はおまけ的な問題ばかりなので省略します。(4)はこんな感じですかね。
100 INPUT PROMPT "p=": P l10 INPUT PROMPT "q=": Q 120 INPUT PROMPT "d=": D 130 LET U=0 140 FOR K=1 TO D 150 LET V=0 160 FOR M=O TO INT(K/P) 170 LET R=K-M*P 180 IF R-INT(R/Q)*Q=0 THEN LET V=V+1 190 NEXT M 210 PRINT "K=" ;K; "のとき, " ;V; "個" 220 LET U=U+V 230 NEXT K 240 PRINT "総数="; U 250 END
言語を変えてみると
ここで終わったら、ただの試験解説参考書と同じなので、言語を変えて表現してみます。
いつものC言語にしてみると
#include <stdio.h> int main(){ int p,q,d; int u; int k; int r; int m; printf("p="); scanf("%d",&p); printf("q="); scanf("%d",&q); printf("d="); scanf("%d",&d); u = 0; for(k = 1; k <= d; k++){ if(k - k/p*p == 0) goto LABEL; for(m = 0; m <= k/p; m++){ r = k - m * p; if(r - r/q*q == 0) goto LABEL; } continue; LABEL: printf("%d\n", k); u++; } printf("総数=%d\n", u); return 0; }
こんな感じ。INT()は不要になります。また、goto文を使わずに書くと、
#include <stdio.h> int main(){ int p,q,d; int u; int k; int r; int m; int flag; printf("p="); scanf("%d",&p); printf("q="); scanf("%d",&q); printf("d="); scanf("%d",&d); u = 0; for(k = 1; k <= d; k++){ if(k % p != 0){ flag = 0; for(m = 0; m <= k/p; m++){ r = k - m * p; if(r % q == 0){ flag = 1; break; } } if(flag == 0){ continue; } } printf("%d\n",k); u++; } printf("総数=%d\n",u); return 0; }
みたいになります。ちょっと雰囲気が変わりますね。
22年度問題
これを書いているときにちょうど22年度の問題が公開されたようなので、見てみましょう。
[前提: a + b + c = N (a≦b≦c)、Nを表す総数を求めるプログラムを作成する。前問でa≦N/3であることを答えている]
100 INPUT N 110 LET X=0 120 FOR A=1 TO INT(N/[ ア ]) 130 LET [ エ ] 140 NEXT A 150 PRINT "N=";N;"のとき, 総数は";X;"通りである"
Aをループで回しているので、BとCでN-Aの数を分割すればいいです。Bが決まれば自然にCが決まるので、Bのみを考えればいいです。Bのとりうる数は単純に考えてN-A通り。しかし、B≦Cの条件により、組み合わせが半減して(N-A)/2通りとなり、さらに、A≦Bより、(N-A)/2-A+1通りとなります(+1はA=Bのケース)。ゆえに、[ エ ]に入るのはX=X+INT((N-A)/2)-A+1、5番となります。
続いて問題は二等辺三角形へと発展していきます。
一般に, 三つの正の数について, どの二つの数の和も残りの数より大きければ、それを三辺の長さとする三角形が存在する。逆に, すべての三角形において, どの二辺の長さの和も残りの一辺の長さより大きい。
だそうで、先ほどのプログラムの130行を削除して、131~137行を追加すればいいとのこと。
131 FOR B=[ ク ] 132 LET C=[ ケ ] 133 IF [ コ ] THEN 134 PRINT "(";A;",";B;",";C;")" 135 LET [ サ ] 136 END IF 137 NEXT B
BはA以上、C以下、ただし、ここではCはでてきていないので、4番の「A TO INT((N-A)/2)」がもっとも適当でしょう。
[ ケ ]はそのままCの条件を考えて4番の「N-A-B」、[ コ ]はA≦B≦Cで、短い辺2つの和が長い辺より長ければOKだから、2番の「C<A+B」、[ サ ]はカウントしているだけなので、5番の「X=X+1」。この数が最終的に「総数は~である」と表示されます。まあ、分かっちゃえば簡単な問題ですね。これを試験場の緊張した雰囲気の中で解くとなると、また違うのでしょうが。
さて、このプログラムをC言語にすると
#include <stdio.h> int main(){ int n, a, b, c, x; scanf("%d",&n); x = 0; for(a = 1; a <= n/3; a++){ for(b = a; b <= (n - a) / 2; b++){ c = n - a - b; if(c < a + b){ printf("(%d,%d,%d)\n",a,b,c); x++; } } } printf("N=%dのとき, 総数は%d通りである\n",n,x); return 0; }
シンプルに書けます。scanf()はfgets()+atoi()の組み合わせでもOK。そちらの方が安全になります。
では、これをJavaにすると・・・
import java.io.*; public class Main { /** * @param args the command line arguments */ public static void main(String[] args) throws IOException { String strN; int N, A, B, C, X; BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); strN = in.readLine(); N = Integer.valueOf(strN); X = 0; for(A = 0; A <= N/3; A++){ for(B = A; B <= (N - A) / 2; B++){ C = N - A - B; if(C < A + B){ System.out.println("(" + A + "," + B + "," + C + ")"); X++; } } } System.out.println("N=" + N + "のとき, 総数は" + X + "通りである"); } }
のようになります。例外スロー(in.readLine()でIO例外が発生する恐れがある)とか、クラスからインスタンスの作成とか、ちょっと面倒ごとが増えていますが、大規模なプログラムを書くときはこういったオブジェクト指向言語の方が楽だったりします。
※メール、リンクは適当に。
Copyright (c) 2009, 2010 greencap