💭

真偽値を作る

2025/03/23に公開

if文を用いずに真偽値を作成します
真偽値はtrue=1,false=0と表されますが、これらの値は数式的にどのように導けるでしょうか
ここではプログラミング的手法と数学的手法の二つ方法からアプローチを試みます

前提

冒頭にtrue=1を示しましたが、これはより厳密にtrue>0変数とみなせます
つまりある計算式の0を超過する全ての和や積を1に丸められれば、それ自身でtrueを表現できます
ここで、これら丸められた数に対応する列挙子を予め用意しておきます

C#
public enum BoolFlag:byte{
	FALSE,
	TRUE
}

プログラミング的手法

ここでは補数の概念を借用します
プログラミングでは、全ての値にはがあります
数値も同様に、型によって値の大きさを規格化します
負の数は、この規格の中で扱える最大値に対する補数として考えます
-1はある型の最大値から1を引いた数です
ならば、その-1はより上位の型において巨大な正の整数として扱われるはずです

BigIntegeを用いた真偽表現
public static class BoolOP{
        static int ToBoolNum(BigInteger num)=>(int)((num+2)%(num+1));
        public static int ToBoolNum(long num)=>ToBoolNum(unchecked((ulong)num));
	
        public static BoolFlag Equal(this long x,long y)
		=>(BoolFlag)(ToBoolNum((BigInteger)unchecked((ulong)x)+1)-ToBoolNum(x-y));
	
	public static BoolFlag IsBig(this long x,long y){
		long seed=y-x;
		BigInteger a=unchecked((ulong)seed),b=seed;
		return (BoolFlag)ToBoolNum(a-b);
	}
	
	public static BoolFlag AsBig(this long x,long y){
		int a=(int)Equal(x,y),b=(int)IsBig(x,y);		
		return (BoolFlag)ToBoolNum(a+b);
	}
	
	public static BoolFlag IsLittle(this long x,long y)=>IsBig(y,x);
	public static BoolFlag AsLittle(this long x,long y)=>AsBig(y,x);
}

public enum BoolFlag:byte{
	FALSE,
	TRUE
}

注意すべきは補数を扱うために和や積より上位の型が必須である点です
上のコードではC#で一般的な最大の整数型であるlongを扱っていますが、これを評価するにはより上位のBigIntegerが必要です
またこの原則よりBigInteger自身は評価不能です
-1BigIntegerの最大値から1を引いた補数と仮定すると、これに対する加算がBigIntegerの最大値を超過することに繋がるからです
C#の場合、longulongにキャストすることで符号なしの整数、すなはち負の整数の補数を取得できます
オーバーフローによってコンパイルエラーに引っかからないように、ここではunchecked()演算子を用いて最大値検閲を無効化します
それぞれのメソッドは、各演算子に対応させて呼び出します
これを扱う構造体として、ここではNumberを考えます

public struct Number(long number){
        long _number=number;
	readonly string _word;
        BoolFlag Flag{get;init;}=BoolFlag.FALSE;
	BoolFlag StrBool{get;init;}=BoolFlag.FALSE;
	public Number(double number):this(Unsafe.As<double,long>(ref number))=>Flag=BoolFlag.TRUE;
	public Number(string str):this(unchecked(str.Select(x=>(int)x).Sum()))=>(StrBool,this._word)=(BoolFlag.TRUE,str);
	public object GetNumber=>new object[]{new ValueType[]{_number,Unsafe.As<long,double>(ref _number)}[(int)Flag],_word}[(int)StrBool];
	public static implicit operator Number(long x)=>new(x);
	public static implicit operator Number(double x)=>new(x);
	public static implicit operator Number(string word)=>new(word);
	public static explicit operator Type(Number num)=>new[]{new[]{typeof(long),typeof(double)}[(int)num.Flag],typeof(string)}[(int)num.StrBool];
	public static implicit operator string(Number num)=>((Type)num).Name;
	public static BoolFlag operator <(Number x,Number y)=>x._number.IsLittle(y._number);
	public static BoolFlag operator >(Number x,Number y)=>x._number.IsBig(y._number);
	public static BoolFlag operator ==(Number x,Number y)=>x._number.Equal(y._number);
	public static BoolFlag operator !=(Number x,Number y)=>(BoolFlag)ToBoolNum(x._number-y._number);
}
全体コード
C#
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Linq;
using static BoolOP;
					
public class Program
{
	public static void Main()
	{
		Number a="abc",b="abc";
		Console.WriteLine(a.GetNumber);
	}
}

public struct Number(long number){
        long _number=number;
	readonly string _word;
        BoolFlag Flag{get;init;}=BoolFlag.FALSE;
	BoolFlag StrBool{get;init;}=BoolFlag.FALSE;
	public Number(double number):this(Unsafe.As<double,long>(ref number))=>Flag=BoolFlag.TRUE;
	public Number(string str):this(unchecked(str.Select(x=>(int)x).Sum()))=>(StrBool,this._word)=(BoolFlag.TRUE,str);
	public object GetNumber=>new object[]{new ValueType[]{_number,Unsafe.As<long,double>(ref _number)}[(int)Flag],_word}[(int)StrBool];
	public static implicit operator Number(long x)=>new(x);
	public static implicit operator Number(double x)=>new(x);
	public static implicit operator Number(string word)=>new(word);
	public static explicit operator Type(Number num)=>new[]{new[]{typeof(long),typeof(double)}[(int)num.Flag],typeof(string)}[(int)num.StrBool];
	public static implicit operator string(Number num)=>((Type)num).Name;
	public static BoolFlag operator <(Number x,Number y)=>x._number.IsLittle(y._number);
	public static BoolFlag operator >(Number x,Number y)=>x._number.IsBig(y._number);
	public static BoolFlag operator ==(Number x,Number y)=>x._number.Equal(y._number);
	public static BoolFlag operator !=(Number x,Number y)=>(BoolFlag)ToBoolNum(x._number-y._number);
}

public static class BoolOP{
        static int ToBoolNum(BigInteger num)=>(int)((num+2)%(num+1));
        public static int ToBoolNum(long num)=>ToBoolNum(unchecked((ulong)num));
	
        public static BoolFlag Equal(this long x,long y)
		=>(BoolFlag)(ToBoolNum((BigInteger)unchecked((ulong)x)+1)-ToBoolNum(x-y));
	
	public static BoolFlag IsBig(this long x,long y){
		long seed=y-x;
		BigInteger a=unchecked((ulong)seed),b=seed;
		return (BoolFlag)ToBoolNum(a-b);
	}
	
	public static BoolFlag AsBig(this long x,long y){
		int a=(int)Equal(x,y),b=(int)IsBig(x,y);		
		return (BoolFlag)ToBoolNum(a+b);
	}
	
	public static BoolFlag IsLittle(this long x,long y)=>IsBig(y,x);
	public static BoolFlag AsLittle(this long x,long y)=>AsBig(y,x);
}

public enum BoolFlag:byte{
	FALSE,
	TRUE
}
Console
abc

Numberは整数を用いることで、浮動小数や文字列の大小関係も表現可能なことを示しています
上位の整数型の補数という概念を導入することで、プログラミング的にif文が全く存在しない真偽判定がこれで構築できます

数学的手法

補数は数学にも登場する概念ですが、数学では整数に型という規格を基本的には採用しません
つまり数学的に上位の整数型を考える場合は、補数は♾️-nと捉えれば良さそうです
ですがこの他の方法でも真偽値の表現は可能です
ここでは補数の代替として絶対値を採用します

Haskell
modNum::Int->Int->Int
modNum x y=x-y*(x `div` y)

boolNum::Int->Int
boolNum x=modNum ((+2).abs $ x) ((+1).abs $ x)

equal::Int->Int->BoolFlag
equal x y=toEnum.boolNum $ (boolNum.(+1).abs $ x)-(boolNum $ x-y)

isBig::Int->Int->BoolFlag
isBig x y=(\ number->toEnum.boolNum.($ number).(-).abs $ number) $ y-x

asBig::Int->Int->BoolFlag
asBig x y=toEnum.boolNum $ (fromEnum $ equal x y)+(fromEnum $ isBig x y)

isLittle::Int->Int->BoolFlag
isLittle x y=isBig y x

asLittle::Int->Int->BoolFlag
asLittle x y=asBig y x

signe::Int->Int->Int
signe x y=(fromEnum $ isBig x y)-(fromEnum $ isLittle x y)

data BoolFlag=False|True deriving (Enum,Show)

数学への造詣が浅いため、ここではAIによる数式への置換をイメージとして示します

Gemini
1. `modNum x y`

   `modNum(x, y) = x - y * floor(x / y)`

2. `boolNum x`

   `boolNum(x) = |(|x| + 2) mod (|x| + 1)|`

3. `equal x y`

   `equal(x, y) = boolNum(boolNum(|x| + 1) - boolNum(x - y))`

4. `isBig x y`

   `isBig(x, y) = boolNum(y - x)`

5. `asBig x y`

   `asBig(x, y) = boolNum(equal(x, y) + isBig(x, y))`

6. `isLittle x y`

   `isLittle(x, y) = isBig(y, x)`

7. `asLittle x y`

   `asLittle(x, y) = asBig(y, x)`

8. `signe x y`

   `signe(x, y) = isBig(x, y) - isLittle(x, y)`

9. `BoolFlag` データ型

   * `False`
   * `True`

あとはC#のように整数以外の値を整数として再解釈する手段を実装すれば、整数型の表現規則に囚われない汎用的な真偽値の導出法が構築できます
ただしプログラミング的には絶対値などの算出に内部的な条件分岐を活用しているため、完全な置き換えとしては今一歩不足します

総括

以上、真偽値を手動で実装する方法について考えました
応用範囲は限定的ながら、真偽処理を自己完結できる点においては有用です
条件分岐の一切を禁じられた時に、抜け道として利用すると良いでしょう

Discussion