2025年3月13日木曜日

奇数か(偶数か)を判定する

前置き:

今回のお話は 𝕏のおすすめ欄にたまたま流れてきたポスト「1~100までの整数が奇数かどうかを判定するプログラムを書きました!」に起因します。

一般の人には面白さが伝わるか分からないけど『「もし奇数なtrueそうでなければfalse」つまり1なら奇数2なら偶数3なら偶s...って100個書けば良いんだ!』

HAHAHAそうだけどそうじゃないだろっていう面白さ…やっぱり伝わらないかも。

確かに定義が「1~100固定で奇数ならtrueを返し偶数ならfalse」なら間違いないくこれで答えが得られるけど汎用性が無さすぎる事が1つ、もう一つはそもそも処理本質はそこじゃない。っていう所。

「1なら奇数」...は既に答えを知っている場合の条件分岐で本来定義を考えるべきなのは奇数はどの条件で奇数になるのかって話。

このポストにはさらに元ネタがあって「コーディングのヒント」っていうこのベタif文をエクセル等のオートフィル(処理を繰り返す)によって脳筋コードを生成するってネタポストの再ネタポストって事ですね。

そして「【ゆる募】1~100までの整数が奇数かどうかを判定するプログラム大喜利を開催します☺ 言語不問✨」更にネタにした二番煎じ、三番煎じのネタポスト『無駄に洗練された無駄の無い無駄な動き』を考える会みたいな話です


「プログラムをする」=「ロジカルな思考になる事」

自身はプログラマーではないく精々スクリプターなので思考が雑だけれど基本的には同じで「コーディング」する事よりも「その本質はなんなのか」を考える思考ロジックがたぶん一般人とプログラマーの境界な気がします。

この記事を書くきっかけになったのはネタポストもあるけれど実は大喜利のリプにあった「入力が数値じゃないかもしれない」みたいなポストがあってそうじゃないんよなぁと思ってなんとなく(今はそういう話をしてるんではなくロジック部分のみの話なんよ)。

さておき、そもそも通常はどのようにコードを書くか


n=99;print(bool(n%2))


最初に考えたのはこんな感じです。ポスト元が1をtrueにしてるのでそれに合わせます。

奇数なら余りは1になり、偶数なら余りが0になります。

これをbool値「真(True)偽(False)」に変換します。

まぁでもよくよく考えたら「n%2==1」で直接bool値になりますね。

元のコードの通り関数にするなら


num=99
def is_odd(num:int)->bool:return num%2==1
print(is_odd(n))


こんな感じでしょうか。

この通り本来は奇数をだすならたったこれだけなのです。


大喜利の話

特に記載はないですがこの手の大喜利の要点は

結果が正しい事は前提で、より壮大に無駄にリソースを使い条件を満たそうという話です。

かつ限定条件が1~100なのでこの範囲じゃないと上手く動かないとかの方が面白い。

更に言えば単に意味のない処理を入れる訳ではなく必要な処理で構成されており、処理が抜けると動かない形であった方が良いですね。


まぁなのである意味一番元のベタif文100個並べるが完成されてるかもしれません

(判定を100回行い、なおかつ1行でも抜けると正しく動作しない)


もう一つの要点

それは定義を考えるという事です。

通常の奇数の定義は「数値に対して2で割って余りが1なら奇数」これが普通です。

このような大喜利はこの定義を別の条件に言い換え処理を考える遊びです。


例えば入力は1~100の整数という条件ですので「奇数は偶数と必ず交互に存在する」見たいな事を考えた場合「奇数から始まり、対象の数値まで真と偽を交互に代入」すれば奇数か偶数かを判定できる。

𝕏では花占いと表現してポストしたやつで


n=100;b=false;for(i=0;i<n;i++){if(b){b=false}else{b=true}}


こんな感じです。

Jscriptですがまぁjavascriptでも同じです。


更に別の奇数の定義を考えてみます。

交互に存在するなら「真偽交互に100個分回答を用意して、入力数値番目」にアクセスすれば直接真偽を得られる


n=99;WScript.StdOut.WriteLine(Boolean("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010".split("")[n]*1))


定義を元に少し変更します。

画像でポストするのが面倒だしコピペ出来ないし面倒。という事でコードを「全角140文字=半角280字で考える」

なので真偽配列を10二進数配列を作ります。

さらに配列は0から始まるけど入力値は1~100なので要素-1のアクセスが面倒なので配列の0番目の要素を追加し101個の配列を用意してやれば入力値で直接0か1が返ります。これをbool変換すれば奇数か判定できる。 コードはJscriptのCscriptです。


他にも大喜利の例題は「1,3,5,....99なら奇数」という50個分の判定ですがそもそも偶数奇数判定は「入力値の末尾1桁が1,3,5,7,9なら奇数」でいいじゃんと考える事も出来ます。

であれば


n=100;print([False,True,False,True,False,True,False,True,False,True][int(list(str(n))[-1])])


こんな感じで真偽100個配列を作るより奇数5つの配列でいい 今度はPythonで組んでみましたが先ほどやってる事は同じです。


あと他には「2で割って余りが1なら奇数」という表現を別の表現で考える方法もあります

「2で割った結果が小数になるならば奇数」みたいな感じです


n=100:msgbox cbool(ubound(split(n/2,".")))


例えば2で割ってそれを小数点で配列化、割り切れてれば配列数は0、割り切れてなければ1。

0・1をbool化すれば真偽判定できます


言語特有の考え方ですが先ほどの100個全ての真偽を配列にする方法を

n=100;print(bool(int(list("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010")[n])))

Python版で考えて、Pythonは進数変換が容易なのでこの二進数を16進数に圧縮しようかと思ったんですがそもそも


n=100;print(bool(int(list("01"*50+"0")[n])))


の圧縮率が高すぎてもうこれで良くない?みたいな事もあります。

「01」というパターンを50回繰り返すというPython記述便利。


さて、交互に繰り返される真偽・二進数・末尾一桁と来ましたが入力値が0かnot0になれば良いわけです。だとするとそもそも「十進数を二進数に変換して末尾一桁を真偽変換」すれば良いのではとも気づきます。


n=100;print(bin(n)[-1]=="1")


おっと、また短くなってしまった。Python久々に書いたけど便利だなぁ。

もっと無駄な形にしないとダメですね


割り算と余りの概念を脳筋で解決する方法も考えてみます

2個をワンセットとして対消滅させていき、数字君が生き残れれば奇数

分かりやすく言うと「数値を2ずつ引いていき、余れば奇数」


n=99:do while n>1:n=n-2:loop:msgbox n=1

vba・vbsならこんな感じです。これもやっぱり短いですね。
やはりベタifが無駄であり無駄という意味では酷いコードの完成度が高い。
でも𝕏の文字数制限内でそれ書くのは面倒臭い。
じゃあ、いっそ実行可能な形式でそれを生成すればいいよね

a=split("false/true","/"):c="n=数値"&vbcrlf:for i=1 to 100:c=c&"if n="&i&" then b="&a(i mod 2)& vbcrlf:next:c=c&"msgbox b"&vbcrlf
createobject("scripting.filesystemobject").createtextfile("n.vbs", 1).write c


𝕏の文字数制限内に収まりました
「数値」の所を1~100に置き換えればそのまま実行可能です。

if n=1 then b=true
if n=2 then b=false
...

をベタ入力したvbsファイルを生成するvbsです。
コードを生成するコードの時点で二度手間で、コード生成時にi mod 2で既に処理内で余りを出してる所にも二度手間感があります。
「でも何かを抜くと動かなくなる」というコードです。
まぁ一旦こんなところで終わりにしましょう。


意味はないけど意味がある事

今回の「奇数の真偽を出す」は別に意味のある事ではありません
ただ、こう言った処理定義の考え方変換や柔軟な発想、手段の引き出しを増やす事はあるほど良いです。

また、0と1で百個用意して配列要素で値をえる方法などは既に結果が変わらない場合などであれば配列をメモリに突っ込んで引き出した方がforなどを回すより高速に処理できるし、呼び出しが外にあればメインは要素1つ与えるだけで取り出せるメイン処理の簡素化が出来たりもします。

こういったものは別に大喜利である必要はありませんんが内容が簡単で初心者でも上級者でも一緒に頭の体操が出来るのが面白い。
簡単な問題なので「処理すべき事」と「結果がこうなる」が分かっているうえで他の人のコードを見れるから初心者からでも上級者がどんな工程を踏むのかが見れるのも良いです。

他にも「与えられた数値が素数かどうか判定する」「うるう年の判定」など条件を知っているが考え方が試されるものは色々あります。

余談:
上記コードは自分で考えたもので、記述出来るのがwscript、VBA、Pythonなどスクリプト系なのでコードがごった煮でごめんね:;(∩´﹏`∩);:

0 件のコメント:

コメントを投稿