わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

ゼロイチゼロイチ...

 いきなりですが問題です.以下のプログラムの出力が「1 -1 1 -1 1 -1 1 -1 1 -1」となるようにするには,/* ??? */と書いた箇所にどのようなコードを書けばいいでしょうか.

#include <stdio.h>

int main(void)
{
  int i, x = 1;

  for (i = 0; i <= 10; i++) {
    printf("%d ", x);
    /* ??? */
  }
  printf("\n");

  return 0;
}

 さっそくですが解答です.「x = -x;」と書けばいいでしょう.
 この文を実行するごとに,

  • (実行前の)xの値が1なら,xには-1が代入される
  • (実行前の)xの値が-1なら,xには1が代入される

ので,出力は「1 -1 1 -1 1 -1 1 -1 1 -1」となります.
 続いてですが問題です.上のプログラムを修正して,プログラムの出力が「0 1 0 1 0 1 0 1 0 1」となるようにします.まず,「x = 1」を「x = 0」に変更します.そして,/* ??? */と書いた箇所にどのようなコードを書けばいいでしょうか.
 これについても解答です.「x = 1 - x;」と書きます.上と同様に,次のことが言えます.

  • (実行前の)xの値が0なら,xには1 - 0の計算結果である,1が代入される
  • (実行前の)xの値が1なら,xには1 - 1の計算結果である,0が代入される

 なお,「x = x - 1;」ではありません.これだと,出力は「0 -1 -2 -3 -4 -5 -6 -7 -8 -9」となります.
 「x = 1 - x;」の形の代入は,毎年,プログラミング科目でC言語を扱うときの最終回に使用しています.AliceとBobによる,おはじき取りのゲームです.一度に取るのは1個か2個か3個のいずれかとし,最後に取ったほうが負けというルールで,はじめに個数の値をscanfで獲得し,1回分のゲームを実施する(printfで経過を出力していく)というプログラムです.
 プログラムではint p = 0;と宣言して初期化し,この変数の値が0のときはAlice,1のときはBobに対応付けます(playerのpです).単一のwhileループの中で,残り個数をもとに取る数を決め,「Alice took 3」といった文を出力*1し,それから「p = 1 - p;」を実行します.おはじきの残り個数は別の変数で管理していて,その値が0になると,whileループ*2を抜けまして,そのときのpの値は,最後におはじきを取った人の反対,ということで勝者を意味し,「Winner is Alice」または「Winner is Bob」を出力します.おはじきを取る個数を返すのと,上記のpの値に応じて「Alice」または「Bob」を出力するのは,それぞれ関数を定義してmain関数から呼び出します.
 最初の問題のように,「1 -1 1 -1 1 -1 1 -1 1 -1」と変数の値が変化するプログラムは,授業では使用していませんが,例えば数学の偶置換・奇置換における置換の符号*3を,自前で計算するのに,有用となるかもしれません.
 ここまでを踏まえて最後に,問題です.先ほどの「x = 1 - x;」を変更して,プログラムの出力が「0 -1 0 -1 0 -1 0 -1 0 -1」となるようにします.どのようなコードを書けばいいでしょうか.
 これについても解答です.負の数に関して,2の補数表現が採用されていることを前提とするなら,「x = ~x;」と,ビット反転の単項演算子~*4を使用するのが最も簡潔です.2の補数表現でない場合でも大丈夫なようにしようとすると,「x = -1 - x;」と書けばいいでしょう*5.以下が成り立ちます.

  • (実行前の)xの値が0なら,xには-1が代入される
  • (実行前の)xの値が-1なら,xには0が代入される

*1:昨年度までに授業を受けてこの記事を見た人へ.Linux使用の際には日本語メッセージを出力させていましたが,今年度のコマンドラインインタフェースだと,日本語メッセージは文字化けしまして,今回,メッセージはすべてASCII文字で出力するよう,プログラムを変更したのでした.

*2:このループの中に,「Aliceの番」と「Bobの番」の処理を順番に書いているわけではありません.ループ内は「AliceまたはBobの番」の処理にしていまして,pの値が0ならAliceの番,1ならBobの番を意味します.

*3:https://mathtrain.jp/permutation

*4:この演算子は,論理否定の単項演算子!と別物です.ところで,論理演算の評価結果は0または1になることに注意すると,「0 1 0 1 0 1 0 1 0 1」を出力させる問題について「x = !x;」も正解と言えます.

*5:空白はあってもなくても問題ありませんが,「2項演算子は前後に空白を入れる」「単項演算子とそのオペランドの間には空白を入れない」という慣例にもとづくと,「x = -1 - x;」と書くことになります.