ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

スミス先生のプログラミング教室コミュの初級講座1時間目

  • mixiチェック
  • このエントリーをはてなブックマークに追加
    ,.-=== 、__
   ∠ミミヾj┴彡ゝ
  ,/ ,-ー'"   ヾヨ、
  |/        /7ミ!
  }|r、       l ゙iミ」
  |]ム    _∠ニ,_ィト}
  ト、 ̄ ミl=r"  _/ |hj  それでは授業を始める
  `!  ̄ j ,」 ̄  jr'
   丶 ,_`;..__  i ハ_
    ヽ ゙ー‐ ` ; /"八
     lヽ,_, /// \

コメント(48)

とりえあず、いままで出たお題の回答は随時受け付け中。
できた人から題名を添えてどんどん貼り付けていくこと。
尚、タブは全角スペースに置き換えて貼ると良い。

ちなみに円周率を求めるプログラムの題名は「円周率」とする。

では次のお題。

ファイルを読み込み、内容から半角アルファベットの個数を
数え、表示するプログラムを作りなさい。
ただし、大文字(a〜z)と、小文字(a〜z)を別々にカウントし、
それぞれの個数と、両方を合計した値の3つを表示すること。
題名は「英文字カウント」とする。
誤:大文字(a〜z)
正:大文字(A〜Z)

>>11
出力すべき値は3つなので、アルファベット個別に数える必要な無い。
あと、c[128]だと日本語が混ざっていた時にcore吐くのでだめです。
>>10
ここは講義スレなので間違いが許れます。動かなくてもどんどん貼り付けるよろし。
引き続き3つめのお題。

日付(年月日時分秒)と秒数(64bit整数型)を入力すると、
入力した日付に秒数を足した日付を表示する
プログラムを作りなさい。

ただし、閏年、閏秒までを正確に計算すること。
いつものように入出力以外の標準関数は一切使用禁止とする。
また、入力によって実行時間に差がでてはいけない。

題名は「日付計算」とする。

余力があるなら以下の問題にも挑戦してみること。

追加問題:
 2つの日付を入力し、差を秒単位で求めるプログラムを
 作りなさい。

尚、64bit整数型は、Linuxではlong long、Windowsでは
_int64とすれば宣言することができる。
黙々とお題4つ目。

5枚のトランプのカードを入力させ、ポーカーの役に
なっているかを判定するプログラムを作りなさい。

以下の注意事項を遵守すること。

・5枚のカードの入力方法は各自で考案し、実装すること。
・内部でのデータ表現も各自で考案し、実装すること。
・入力方法並びにデータ表現方法はコメントで説明を加えること。
・同じ種類のカードが入力された場合はエラーを出すこと。
・ジョーカーは無しで良い。
・ポーカーの役のルールをよく調べておくこと。

題名は「ポーカーの役判定」とします。
人数は日々増えているのですが書き込みが一向に増えないのは課題が難しすぎるせいなんでしょうか。

そう思うと思った方は0と書き込んでください。
いや、そうじゃないと思った方は0以外を書き込んでください。

題名は「第一回目安箱」とします。
     _,,...,_
  /_~,,..::: ~"'ヽ
 (,,"ヾ  ii /^',)
    :i    i"
    |(,,゚Д゚)  <第一回目安箱
    |(ノ  |)
    |    |
    ヽ _ノ
     U"U


#include <stdio.h>

int main()
{
printf("コミュの発言率なんてこんなもんだと思います。基本ROMの人多いよ。あんま気にせずキノコってください先生。\n");

return 0;
}
今、忙しい中時間を見つけて、課題に取り組んでいます。
>題名は「日付計算」とする。
なんて、業務仕様そのものですねぇ。がむばってみます。
ROMでした。すいません。
お題1が完成したので発言させていただきます。
これからお題2に入ります。
あ、しまった。嘘です。
お題1完成していません。
標準関数使用している部分があります。
【円周率】
3.141までは一致しました。計算回数を増やせば精度は
あがると思いますが、ひとまず提出します。

#include <stdio.h>
#define COUNT 10000000 /* 計算値 */
#define A 165 /* 乱数発生定数(a to c) */
#define B 201
#define C 131072
double rndm(); /* 乱数発生関数 */
int xn = 100; /* 乱数発生のための初期値 */

/* Pi計算にモンテカルロ法を使用 */
/*
1辺の長さが1の正方形とそれに内接する 1/4の 円 を考える
正方形面積:扇形面積 =1: Pi/4

この正方形の中に,N個(ランダム)の点を落としたとき
R個の点が円内に落ちたとすれば,
1:Pi/4 = N:R

よって Pi = 4*R/N
*/

int main (int argc, const char * argv[]) {
  double x, y;
  int cnt = 0;
  int i;

  for (i = 0; i <= COUNT; i++) {
    /* 乱数を発生させて(0〜1) の(x,y)座標を得る */
    x =rndm();
    y = rndm();

    /* 座標が円内に存在すればカウント */
    if (x * x + y * y <= 1.0)
      cnt++;
    /* 結果出力 */
    if (i != 0 && i % 1000000 == 0)
      printf("%d - %f/n", i, (double)cnt / i * 4.0);
  }
  return 0;
}

double rndm() {
  double ret;

  xn = ((xn * A) + B) % C; /* 線形合同法の式 */
  ret = (double)xn / (double)C; /* 0〜1の値を出力 */
  return ret;
}
>>24 よくできました

結果の精度は乱数を使っている限り、せいぜいその程度です。
乱数といっても擬似乱数なので、余計に精度が悪くなります。

xとyを乱数で決める代わりに、1辺の長さが1.0の正方形を
等間隔に分割した点を使用してみると、もう少しマシな
結果が得られると思います。
せんせ〜とりあえず、日数の加算減算の部分アップします。
#include <stdio.h>
#include <stdlib.h>

void SubDate(char *,char *,int);
int main (int argc,char *argv[]) {

 char inp_ymd[8+2];
 char out_ymd[8+2];
 char day[12];

  /* YYYYMMDD 入力 */

  printf("YYMMDD PLEASE INPIT =>");
  fgets(inp_ymd,12,stdin);

  /* 加減算日数入力 */

  printf("DAYS  PLEASE INPIT =>");
  fgets(day,12,stdin);

  /* 日数計算 */

  SubDate(inp_ymd,out_ymd,atoi(day));

  /* 結果表示 */

  printf("%s\n",out_ymd);

  return (0);
}
void SubDate(char *in_ymd,char *out_ymd,int sub_days) {

 int Mon_Tbl[12]= {31,28,31,30,31,30,31,31,30,31,30,31};
 int all_day,day_cnt;
 int yy,mm,dd;
 int ix;

  /* 入力年月日を全日数変換する */
  sscanf(in_ymd,"%4d%2d%2d",&yy,&mm,&dd);

  Mon_Tbl[1]=((yy%4)?28:((yy%100)?29:((yy%400)?28:29)));
  all_day = 365 * (yy-1);
  all_day += (((yy-1) / 4) - ((yy-1) / 100) + ((yy-1) / 400));
  for (ix=0;ix<mm-1;ix++) {
    all_day += Mon_Tbl[ix];
  }
  all_day += dd;
  /* 差分適用 */
  all_day += sub_days;
  /* 全日数変換された年月日を年月日に戻す */
  for (ix=1,day_cnt=0;;ix++) {
    if (((ix%4)?28:((ix%100)?29:((ix%400)?28:29))) == 28) {
      day_cnt += 365;
    } else {
      day_cnt += 366;
    }
    if ((all_day - day_cnt) < 366) {
      break;
    }
  }
  /* YY 確定 */
  yy = ix+1;
  dd = all_day - day_cnt;
  Mon_Tbl[1]=((yy%4)?28:((yy%100)?29:((yy%400)?28:29)));
  /* MM DD確定 */
  for (ix=0,mm=1;ix<12;ix++) {
    if ((dd - Mon_Tbl[ix]) > 0) {
      mm++;
      dd -= Mon_Tbl[ix];
    } else {
      break;
    }
  }
  if (mm == 1 && dd == 0) { yy--; mm = 12; dd = 31; }
  sprintf(out_ymd,"%04d/%02d/%02d",(int)yy,(int)mm,(int)dd);
}
>>27 再提出

とりあえず気がついた点から。

1:お題では2つ目の入力は「秒数」です

2:
 char inp_ymd[8+2];
 char out_ymd[8+2];

それぞれ1バイトづつ足りません。

sprintf(out_ymd,"%04d/%02d/%02d",(int)yy,(int)mm,(int)dd);

ここで領域外書き込みが発生します。
あと、yy,mm,ddをintでキャストする意味もありません。

3:
 sscanf(in_ymd,"%4d%2d%2d",&yy,&mm,&dd);

この関数はセキュリティ上問題があるので使わないようにしましょう。

4:
 Mon_Tbl[1]=((yy%4)?28:((yy%100)?29:((yy%400)?28:29)));

三項演算子を2重以上にネストするのは避けるべきです。
この場合は素直にif,elseで書いてみましょう。

5:
 for (ix=1,day_cnt=0;;ix++) {

お題の制約「入力によって実行時間に差がでてはいけない。 」
に抵触します。


全体的に処理が複雑になりすぎています。もっとシンプルになる
はずですので再度チャレンジしてみてください。

ヒント:
・入力された日付を紀元(1/1/1 00:00:00)からの秒数に変換
 する関数を作ってみましょう。尚、Unix系システムでは
 1970/1/1 00:00:00を0秒目としています。

・閏年があっても、どこかに周期があるはずです。
 その周期が何秒になるのかを計算して定数にしてみましょう。
せんせ〜再提出用に考えていたら、万年カレンダー作りたくなったので、作ってみました。みてください ヽ(`Д´)ノ

/*******************************************************************************/
/* 機能詳細:CALENDER [YEAR MONTH]                      */
/*      YEARとMONTHが省略された場合は、現在の年月を表示する。      */
/*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define OK   0
#define NG   1
#define ON   1
#define OFF  0
int Mon_Tbl[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
int Year; 
int Mon;  
int Day;  
int Week; 
int SetDate  (char **);
int DispData  (void);

int main(int argc,char *argv[]) {
 int wrk_int;
 if (argc == 3) {
   wrk_int = atoi(argv[1]);
   if ((wrk_int < 1) || (wrk_int > 9999)) {
      printf("年の指定に誤りがあります。[%s]\n",argv[1]); return(NG);
   }
   wrk_int = atoi(argv[2]);
   if ((wrk_int < 1) || (wrk_int > 12))  {
      printf("月の指定に誤りがあります。[%s]\n",argv[2]); return(NG);
   }
   SetDate(argv);

 } else { /* 引数が指定されていない場合 現在の年月をセット */
   SetDate(NULL);
 }
 /* カレンダーの表示                             */
 DispData();
 return(OK);
}
/* カレンダー開始曜日を求める */
int SetDate(char **argv) {
 int idx;
 int wrk_int;
 time_t t;                        /* 通算秒      */
 struct tm *area;                    /* 時間構造体    */
  if (argv) {
    Year = atoi(argv[1]);               /* 年設定      */
    Mon = atoi(argv[2]);               /* 月設定      */
    Day = 0;                     /* 日設定 0を設定 */
  } else {
    t = time(NULL); area = localtime(&t);
    Year = area->tm_year + 1900;            /* 年設定      */
    Mon = area->tm_mon + 1;             /* 月設定      */
    Day = area->tm_mday;               /* 日設定      */
  }
  /* 当年の2月の末日を取得する */
  if ((Year % 4) == 0) {
    if ((Year % 100) == 0) {
      if ((Year % 400) == 0) {
        Mon_Tbl[1]  = 29; 
      } else {
        Mon_Tbl[1]  = 28; 
      }
    } else {
      Mon_Tbl[1]  = 29; 
    }
  } else {
    Mon_Tbl[1]  = 28; 
  }
  /* 当年の1/1の曜日を求める (通年は1年毎に1日ずれる)*/
  wrk_int = (Year + ((Year-1) / 4) - ((Year-1) / 100) + ((Year-1) / 400)) % 7;
  /* 当月の最初の曜日を求める */
  for (idx = 0; idx < (Mon - 1); idx++) { wrk_int += Mon_Tbl[idx]; }
  Week = wrk_int % 7;
  return(OK);
}
/* カレンダーの明細部の表示 7(曜日)× 6(週間)の表示領域に出力する   */
int DispData () {
 int ix_day,ix_week,ix_line;
 int write_flg;
  /* カレンダーのヘッダ部の表示 */
  printf ("   %04d年%02d月\n",Year,Mon);
  printf (" 日 月 火 水 木 金 土");
  /* 日を表示する */
  ix_day=1; write_flg=OFF;
  for (ix_line=0; ix_line < 6; ix_line++)   { /* 6段分ループする */
    printf("\n");
    for (ix_week=0; ix_week < 7; ix_week++) { /* 曜日分ループする */
      /* 規定日数出力したら終了 */
      if (ix_day > Mon_Tbl[Mon-1]) { break; }
      /* 月の1日の曜日に達したら出力開始 */
      if (Week <= ix_week) { write_flg = ON; }
      if (write_flg == ON)  { /* 日数の書込 */
        printf(" %2d",ix_day);
        ix_day++;
      } else {         /* 月始のブランク書込 */
        printf("  ");
      }
    }
  }
  return(OK);
}
せんせ〜 ぬるモルスァ さんのプログラムは違うと思います!
日本語の2バイト目の英字もカウントしてしまいます。
>>29 ちょっと長いので明日の夜あたりに

>>30
日本語の処理までやろうとするとかなり面倒になるので
今回はそこまでやらなくて良いです。入力データに
ASCII文字以外が含まれていないことを前提にして構いません。
というわけで、漢字に対応したお題を作成しました。いかがでしょう〜か?

/******************************************************************************/
/* 機能詳細:英数字カウント                          */
/*      ALPCNT ファイル名                        */
/*      ※最大レコード長は1024バイトとする。ファイルはShift-jisとする  */
/******************************************************************************/
#include <stdio.h>
#define RECMAX 1024
#define OK   0
#define NG   1
#define YES  1
#define NO   0
// 半角英字判定
#define BIGALP(X)  ((unsigned char)(X)>=0x41 && (unsigned char)(X)<=0x5a)
#define SMLALP(X)  ((unsigned char)(X)>=0x61 && (unsigned char)(X)<=0x7a)
#define ISKANJI(X) ((((unsigned char)(X)>=0x81)&&((unsigned char)(X)<=0x9F))|| \
           (((unsigned char)(X)>=0xE0)&&((unsigned char)(X)<=0xFA)))
int Kanji2(char *,char *);

int main(int argc , char *argv[]) {

 __int64 big_cnt = 0;
 __int64 sml_cnt = 0;
 int   ix;
 FILE  *fp;
 char   buf[RECMAX+1];

  if (argc != 2) {
    printf("Usage:alpcnt file_name\n"); return (NG);
  }
if ((fp = fopen(argv[1],"r")) == NULL) {
    printf("%sが開けません\n",argv[1]); return (NG);
}
  while (fgets(buf,RECMAX,fp) != NULL) {
    for (ix = 0; buf[ix] != 0x0a && ix < RECMAX; ix++) {
      if ( BIGALP(buf[ix]) && Kanji2(buf,buf+ix) == NO ) { big_cnt++; }
      if ( SMLALP(buf[ix]) && Kanji2(buf,buf+ix) == NO ) { sml_cnt++; }
    }
  }
fclose(fp);
  printf("大文字 %I64u個 小文字 %I64u個 合計 %I64u個\n",big_cnt,sml_cnt,big_cnt+sml_cnt);
  return (OK);
}
/* 漢字の2バイト目の判断する。2文字目である:YES 1文字目:NO      */
int Kanji2(char *buf,char *chr) {
 int idx,len,ret_code;
  ret_code = NO;
  len = (int)chr - (int)buf;
  for(idx=0;idx<len;) { if (ISKANJI(buf[idx])){ idx+=2; } else { idx++; }}
  if (buf+idx == chr) { ret_code = NO; }
  if (buf+idx > chr) { ret_code = YES;}
  return(ret_code);
}
>>33
そのうち基礎講座を開設します。
まずは >>29 から。

#include <time.h>

これは使用禁止です。

int Mon_Tbl[12] = {31,28,31,30,31,30,31,31,30,31,30,3

グローバルにする意味がありません。
関数内部でconst intで宣言しましょう。

int SetDate  (char **);

横着せず、Year,Mon,Dayを引数で渡しましょう。

int DispData  (void);

この関数はSetDateとまとめるべきです。

printf("年の指定に誤りがあります。[%s]\n",argv[1]); return(NG);

ちゃんと2行に分けましょう。

/* 当年の2月の末日を取得する */
if ((Year % 4) == 0) { ...

もっと短く簡潔に書けるはずです。

int write_flg;

フラグよりカーソルを定義したほうが綺麗になります。

return(OK);

予約語returnに括弧は必要ありません。
>>32
1行が1024文字以上あって、1024文字目に漢字の1byte目があった場合はどうなるのでしょう。

あと、_int64はここで使う意味がありません。
おそらくfopenが2GB以上のファイルに対応していません。
せんせ〜初歩的な質問ですいません!。
カーソルってなんでしょうか? ググッってみたのですがわかりませんでした。
***1234
5678901
2345678

int cursor=4;

for(i=0;i<cursor;i++) printf(" ");

for(day=1;day<31;day++) {
printf("%2d ",day);
if(cursor==7) {
  cursor=0;
  printf("\n");
 }
}

こんな感じ。
では次の課題。題名は「素因数分解」

整数を一つ入力させ、その数を素因数分解して
以下の形式で表示するプログラムを作りなさい。

入力例 291060

模範的出力

291060 = 2^2 x 3^3 x 5 x 7^2 x 11

尚、n^m は「nのm乗」を意味する。

使用許可関数:
 printf
 scanf
制約:
 配列を使ってはならない
先生!質問です。”配列を使ってはいけない。”
ということは素数を格納する配列を定義してはいけないという解釈でよろしいでしょうか?
つまり、自分で素数をまず見つけ出し、指定された数字を見つけ出した素数で割り続け、使った素数を文字列の式に変換するということですね?
>>42

for(n=3;n <= N;n+=2) {

ここをもう一工夫してみましょう。
お題3のヒントが書いてあるんですけど、うまくそれを
プログラムに反映させることができなくて詰まってます。
うーん。
ちょっと放置しちゃったのでそろそろ新しいお題。

題名は「物理シミュレーションその1」

重力を g [m/s^2] とし、
質量 m の物体を角度r,初速 v [m/s] で発射した時の、
t 秒後の位置を出力するプログラムを作成しなさい。
物体の初期位置は座標(0,0)とする。

●入力
・投射角度 r (単位は任意)
・初速 v [m/s]
・シミュレーション時間 t [s]

●出力
・物体の位置座標(x,y) [m]

以下の定数はdefineで定義すること
・重力加速度 g = 9.80619920 [m/s^2]
・分割時間 dt [s] (任意 1ms〜10ms程度)

●制約
・三角関数の使用を許可する。
・シミュレーション方法は任意だが、
 ちゃんとシミュレーションを行うこと。

●注意事項等
・地面、天井は存在しない。
・精度はそれほど気にしなくて良いが、大きな数と
 小さな数同士での加減算を行うと桁落ち誤差が
 発生してプログラムが正常に動作しない可能性が
 あること考慮すること。

●ヒント
・用意すべき変数は、物体の座標(x,y)と加速度(vx,vy)
・時間を分割し、毎回物体の座標に加速度を足すだけでよい
・今回のケースではvxは変化しないが、vyは毎回gの影響を
 受け変化する。ただしdtを掛けることを忘れないこと。

●追加問題
・余力があれば3次元でも挑戦してみよう
>>44
プログラミングをする際のヒント

・問題を分割すること

・問題の解き方を過不足なく定義すること

・コンピュータの気持ちになって考えること

プログラミングに限らず、あらゆる問題を
解決しようとする時のヒント

・分からない事は調べること

・情報を調べる手段を沢山用意しておくこと

・解からないときは何が解からないのかを考えること

・何が解からないのか分からないときは基本に立ち帰ること
題名「財布の最適化」

日本の貨幣は以下の9種類があるとする。
1 5 10 50 100 500 1000 5000 10000 [円]

1・金額を入力すると、各貨幣の枚数が最少となるような
  上記9種類の貨幣の枚数を出力する関数を作成しなさい。

 入力 金額(int)
 出力 枚数(int[9])

2・現在の所持金Aと、商品の値段Bを入力させ、
  支払い後の財布の状態が最適(※)となるような
  支払い方法を出力する関数を作成しなさい。
  尚、初期所持金は最適状態であるものとする。

入力 初期所持金A(int)
入力 商品の値段B(int)
出力 支払い方法(int[9]) ・・・ 各貨幣の枚数

※最適な状態とは、1 10 100 1000の貨幣が4枚以下、
 5 50 500 5000 の貨幣が1枚以下の状態を指す。

・A < B であった場合はエラーを出力すること。

・余力があれば2000円札の存在を考慮したプログラムに
 改良しなさい。
たまには普通に授業してみます。

【ショートサーキット】

論理演算子 && や || は、式を左辺から順に評価し、
それ以降の評価が不要となった場合、右辺を評価しません。
例えば論理和 || の場合、最初の式がtrueであれば
その時点でtrueであることが明確なので、右辺以降が
評価されずtrueと評価されます。

以下のプログラムを実行して動作を確認してみましょう。

int func(int x)
{
  printf("x = %d\n",x);
  return x % 2 ? 1 : 0;
}

main()
{
  if(func(0) || func(1)) printf("A\n");
  if(func(1) || func(2)) printf("B\n");
  if(func(3) && func(4)) printf("C\n");
  if(func(5) && func(6)) printf("D\n");
}

この性質を利用して、ポインタのnullチェックと内容の
チェックを1行で行ったり、評価が確定する確率の高い式や、
処理の軽い検査関数を先にもってきてパフォーマンスを
チューニングするなどといったことができます。

if( p != null && *p!=0 ) return 0;

if( check_light(x) || check_heavy(x) ) return -1;

ログインすると、残り14件のコメントが見れるよ

mixiユーザー
ログインしてコメントしよう!

スミス先生のプログラミング教室 更新情報

スミス先生のプログラミング教室のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。