Cで特定の数値が偶数か奇数かを確認するにはどうすればよいですか?
31 に答える
モジュロ (%) 演算子を使用して、2 で割ったときに剰余があるかどうかを確認します。
if (x % 2) { /* x is odd */ }
x & 1 を使用すると「より高速」または「より効率的」であると述べて、上記の私の回答を批判する人が何人かいます。私はこれが事実であるとは思わない。
好奇心から、2 つの単純なテスト ケース プログラムを作成しました。
/* modulo.c */
#include <stdio.h>
int main(void)
{
int x;
for (x = 0; x < 10; x++)
if (x % 2)
printf("%d is odd\n", x);
return 0;
}
/* and.c */
#include <stdio.h>
int main(void)
{
int x;
for (x = 0; x < 10; x++)
if (x & 1)
printf("%d is odd\n", x);
return 0;
}
次に、これらを私のマシンの1つで5回、gcc 4.1.3でコンパイルしました。
- 最適化フラグなし。
- -O あり
- -Os あり
- -O2あり
- -O3あり
(gcc -S を使用して) 各コンパイルのアセンブリ出力を調べたところ、それぞれの場合で、and.c と modulo.c の出力が同一であることがわかりました (両方とも andl $1, %eax 命令を使用しました)。これが「新しい」機能であるとは思えず、古いバージョンにまでさかのぼると思われます。また、最新の (過去 20 年間に作成された) 非難解なコンパイラ、商用またはオープン ソースに、そのような最適化が欠けているとは思えません。私は他のコンパイラでテストしますが、現時点では利用できません。
他のコンパイラやプラットフォーム ターゲットをテストして、別の結果を得たいと考えている人がいれば、ぜひ知りたいと思います。
最後に、モジュロ バージョンは、符号付き整数の実装の表現に関係なく、整数が正、負、またはゼロのいずれであっても機能することが標準によって保証されています。bitwise-and バージョンはそうではありません。はい、2 の補数はどこにでもあるので、これは実際には問題ではありません。
あなたたちは効率が良すぎるわあああああ。あなたが本当に欲しいのは:
public boolean isOdd(int num) {
int i = 0;
boolean odd = false;
while (i != num) {
odd = !odd;
i = i + 1;
}
return odd;
}
について繰り返しisEven
ます。
もちろん、それは負の数では機能しません。しかし、輝きには犠牲が伴います...
ビット演算を使用します。
if((x & 1) == 0)
printf("EVEN!\n");
else
printf("ODD!\n");
これは、除算やモジュラスを使用するよりも高速です。
【ジョークモード=オン】
public enum Evenness
{
Unknown = 0,
Even = 1,
Odd = 2
}
public static Evenness AnalyzeEvenness(object o)
{
if (o == null)
return Evenness.Unknown;
string foo = o.ToString();
if (String.IsNullOrEmpty(foo))
return Evenness.Unknown;
char bar = foo[foo.Length - 1];
switch (bar)
{
case '0':
case '2':
case '4':
case '6':
case '8':
return Evenness.Even;
case '1':
case '3':
case '5':
case '7':
case '9':
return Evenness.Odd;
default:
return Evenness.Unknown;
}
}
[ジョークモード="オフ"]
編集:列挙型に紛らわしい値を追加しました。
ffpfへの対応- 私は何年も前に同僚とまったく同じ議論をしましたが、答えはnoです。負の数では機能しません。
C 標準では、負の数は次の 3 つの方法で表現できると規定されています。
- 2の補数
- 1の補数
- 符号と大きさ
次のように確認します。
isEven = (x & 1);
2 の補数と符号と大きさの表現では機能しますが、1 の補数では機能しません。
ただし、次のことはすべてのケースで機能すると思います。
isEven = (x & 1) ^ ((-1 & 1) | ((x < 0) ? 0 : 1)));
テキストボックスが私の未満の文字の後にすべてを食べていたことを指摘してくれたffpfに感謝します!
良いものは次のとおりです。
/*forward declaration, C compiles in one pass*/
bool isOdd(unsigned int n);
bool isEven(unsigned int n)
{
if (n == 0)
return true ; // I know 0 is even
else
return isOdd(n-1) ; // n is even if n-1 is odd
}
bool isOdd(unsigned int n)
{
if (n == 0)
return false ;
else
return isEven(n-1) ; // n is odd if n-1 is even
}
このメソッドは、2 つの関数を含む末尾再帰を使用することに注意してください。コンパイラがSchemeコンパイラのような末尾再帰をサポートしている場合、効率的に実装できます(while/untilのようなループに変わります)。この場合、スタックはオーバーフローしてはなりません!
2 で割った余りが 0 の場合は偶数、2 で割った余りが 1 の場合は奇数です。
// Java
public static boolean isOdd(int num){
return num % 2 != 0;
}
/* C */
int isOdd(int num){
return num % 2;
}
メソッドは素晴らしいです!
i % 2 == 0
2 で割り、余りが 0 の場合は偶数、そうでない場合は奇数です。
モジュラス (%) を使用すると、これが簡単になります。
例えば。4 % 2 = 0 したがって 4 は偶数 5 % 2 = 1 したがって 5 は奇数
整数のパリティ (偶数の場合は 0、奇数の場合は 1) のテーブルを作成します (したがって、ルックアップを実行できます:D) が、gcc ではそのようなサイズの配列を作成できません。
typedef unsigned int uint;
char parity_uint [UINT_MAX];
char parity_sint_shifted [((uint) INT_MAX) + ((uint) abs (INT_MIN))];
char* parity_sint = parity_sint_shifted - INT_MIN;
void build_parity_tables () {
char parity = 0;
unsigned int ui;
for (ui = 1; ui <= UINT_MAX; ++ui) {
parity_uint [ui - 1] = parity;
parity = !parity;
}
parity = 0;
int si;
for (si = 1; si <= INT_MAX; ++si) {
parity_sint [si - 1] = parity;
parity = !parity;
}
parity = 1;
for (si = -1; si >= INT_MIN; --si) {
parity_sint [si] = parity;
parity = !parity;
}
}
char uparity (unsigned int n) {
if (n == 0) {
return 0;
}
return parity_uint [n - 1];
}
char sparity (int n) {
if (n == 0) {
return 0;
}
if (n < 0) {
++n;
}
return parity_sint [n - 1];
}
代わりに、代わりに偶数と奇数の数学的定義に頼りましょう。
n = 2k となる整数 k が存在する場合、整数 n は偶数です。
n = 2k + 1 となる整数 k が存在する場合、整数 n は奇数です。
そのコードは次のとおりです。
char even (int n) {
int k;
for (k = INT_MIN; k <= INT_MAX; ++k) {
if (n == 2 * k) {
return 1;
}
}
return 0;
}
char odd (int n) {
int k;
for (k = INT_MIN; k <= INT_MAX; ++k) {
if (n == 2 * k + 1) {
return 1;
}
}
return 0;
}
C 整数int
が、特定の C コンパイルで可能な値を示すとします。(C 整数は整数のサブセットであることに注意してください。)
ここで、C 整数の特定の n に対して、対応する整数 k が C 整数内に存在しないのではないかと心配する人がいるかもしれません。しかし、少し証明すれば、すべての整数 n に対して |n| であることを示すことができます。<= |2n| (*)、ここで |n| 「nが正の場合はn、それ以外の場合は-n」です。言い換えれば、整数のすべての n に対して、少なくとも次のいずれかが成り立ちます (実際にはケース (1 と 2) またはケース (3 と 4) のいずれかですが、ここでは証明しません)。
ケース 1: n <= 2n。
ケース 2: -n <= -2n。
ケース 3: -n <= 2n。
ケース 4: n <= -2n。
ここで 2k = n を取ります。(n が偶数の場合、そのような ak は存在しますが、ここでは証明しません。n が偶数でない場合は、even
いずれにせよループの戻りに失敗するため、問題にはなりません。) しかし、これは、n の場合、k < n を意味します。 (*) による 0 ではないこと、およびすべての m について、整数 2m = z の z は、m が 0 でない場合、z が m と等しくないことを意味するという事実 (ここでも証明されていません)。 n が 0 の場合、2*0 = 0したがって、0 は完了です (n = 0 の場合、n は関数の C 整数にあるため、0 は C 整数にeven
あり、したがって k = 0 は C 整数にあります)。したがって、n が偶数の場合、C 整数の n に対してそのような C 整数の ak が存在します。
同様の議論は、n が奇数の場合、n = 2k + 1 となるような C 整数に ak が存在することを示しています。
したがって、ここに示す関数even
とodd
は、すべての C 整数に対して適切に機能します。
問題に対するもう 1 つの解決策
(子供たちの投票は大歓迎です)
bool isEven(unsigned int x)
{
unsigned int half1 = 0, half2 = 0;
while (x)
{
if (x) { half1++; x--; }
if (x) { half2++; x--; }
}
return half1 == half2;
}
// C#
bool isEven = ((i % 2) == 0);
このかなり面白い議論を読んで、メイン ループ内で奇数と偶数をテストする、時間に敏感な実際の関数があることを思い出しました。次のように、StackOverflow の別の場所に投稿された整数べき乗関数です。ベンチマークは非常に驚くべきものでした。少なくともこの現実世界の関数では、モジュロは遅くなり、大幅に遅くなります。勝者は大差でモジュロの時間の 67% を必要とし、 or ( | ) アプローチであり、このページのどこにも見つかりません。
static dbl IntPow(dbl st0, int x) {
UINT OrMask = UINT_MAX -1;
dbl st1=1.0;
if(0==x) return (dbl)1.0;
while(1 != x) {
if (UINT_MAX == (x|OrMask)) { // if LSB is 1...
//if(x & 1) {
//if(x % 2) {
st1 *= st0;
}
x = x >> 1; // shift x right 1 bit...
st0 *= st0;
}
return st1 * st0;
}
3 億ループの場合のベンチマーク タイミングは次のとおりです。
| 3.962 とマスクアプローチ
4.851 & アプローチ
5.850 % アプローチ
理論、またはアセンブリ言語のリストがこのような議論を解決すると考えている人にとって、これは注意すべき話です。ホレイショ、天と地にはあなたの哲学で夢見ている以上のことがある。
これは、@RocketRoyの答えに関する議論のフォローアップですが、これらの結果を比較したい人には役立つかもしれません。
tl;dr私が見てきたことから、Roy のアプローチ ( ) はアプローチとして(0xFFFFFFFF == (x | 0xFFFFFFFE)
完全に最適化されているわけではありませんが、実際には実行時間はすべての場合で等しくなるはずです。x & 1
mod
そのため、最初にCompiler Explorerを使用してコンパイルされた出力を比較しました。
テストされた機能:
int isOdd_mod(unsigned x) {
return (x % 2);
}
int isOdd_and(unsigned x) {
return (x & 1);
}
int isOdd_or(unsigned x) {
return (0xFFFFFFFF == (x | 0xFFFFFFFE));
}
-O3 を指定した CLang 3.9.0:
isOdd_mod(unsigned int): # @isOdd_mod(unsigned int)
and edi, 1
mov eax, edi
ret
isOdd_and(unsigned int): # @isOdd_and(unsigned int)
and edi, 1
mov eax, edi
ret
isOdd_or(unsigned int): # @isOdd_or(unsigned int)
and edi, 1
mov eax, edi
ret
-O3 を使用した GCC 6.2:
isOdd_mod(unsigned int):
mov eax, edi
and eax, 1
ret
isOdd_and(unsigned int):
mov eax, edi
and eax, 1
ret
isOdd_or(unsigned int):
or edi, -2
xor eax, eax
cmp edi, -1
sete al
ret
CLang に敬意を表し、3 つのケースすべてが機能的に同等であることに気付きました。ただし、Roy のアプローチは GCC では最適化されていないため、YMMV.
Visual Studio の場合も同様です。これら 3 つの関数の逆アセンブリ リリース x64 (VS2015) を調べたところ、「mod」と「and」の場合は比較部分が等しく、Roy の「or」の場合はわずかに大きいことがわかりました。
// x % 2
test bl,1
je (some address)
// x & 1
test bl,1
je (some address)
// Roy's bitwise or
mov eax,ebx
or eax,0FFFFFFFEh
cmp eax,0FFFFFFFFh
jne (some address)
ただし、これら 3 つのオプション (plain mod、bitwise または bitwise and) を比較するために実際のベンチマークを実行した後、結果は完全に同等でした (ここでも、Visual Studio 2005 x86/x64、リリース ビルド、デバッガーは接続されていません)。
リリース アセンブリはandケースのtest
命令を使用しますが、Roy のケースはアプローチを使用しますが、かなり展開され最適化されているため、実際には違いはありません。and
mod
cmp eax,0FFFFFFFFh
20 回実行した後の結果 (i7 3610QM、Windows 10 の電源プランを高パフォーマンスに設定):
[テスト: Plain mod 2] 平均時間: 689.29 ms (相対差: +0.000%) [テスト: ビット単位または ] 平均時間: 689.63 ミリ秒 (相対差: +0.048%) [テスト: Bitwise and ] 平均時間: 687.80 ms (相対差: -0.217%)
これらのオプションの違いは 0.3% 未満であるため、すべてのケースでアセンブリが等しいことは明らかです。
誰かが試してみたい場合のコードは次のとおりです。ただし、Windowsでのみテストしたことに注意してください(定義の#if LINUX
条件を確認し、必要に応じて実装してください。この回答から取得しました)。get_time
#include <stdio.h>
#if LINUX
#include <sys/time.h>
#include <sys/resource.h>
double get_time()
{
struct timeval t;
struct timezone tzp;
gettimeofday(&t, &tzp);
return t.tv_sec + t.tv_usec*1e-6;
}
#else
#include <windows.h>
double get_time()
{
LARGE_INTEGER t, f;
QueryPerformanceCounter(&t);
QueryPerformanceFrequency(&f);
return (double)t.QuadPart / (double)f.QuadPart * 1000.0;
}
#endif
#define NUM_ITERATIONS (1000 * 1000 * 1000)
// using a macro to avoid function call overhead
#define Benchmark(accumulator, name, operation) { \
double startTime = get_time(); \
double dummySum = 0.0, elapsed; \
int x; \
for (x = 0; x < NUM_ITERATIONS; x++) { \
if (operation) dummySum += x; \
} \
elapsed = get_time() - startTime; \
accumulator += elapsed; \
if (dummySum > 2000) \
printf("[Test: %-12s] %0.2f ms\r\n", name, elapsed); \
}
void DumpAverage(char *test, double totalTime, double reference)
{
printf("[Test: %-12s] AVERAGE TIME: %0.2f ms (Relative diff.: %+6.3f%%)\r\n",
test, totalTime, (totalTime - reference) / reference * 100.0);
}
int main(void)
{
int repeats = 20;
double runningTimes[3] = { 0 };
int k;
for (k = 0; k < repeats; k++) {
printf("Run %d of %d...\r\n", k + 1, repeats);
Benchmark(runningTimes[0], "Plain mod 2", (x % 2));
Benchmark(runningTimes[1], "Bitwise or", (0xFFFFFFFF == (x | 0xFFFFFFFE)));
Benchmark(runningTimes[2], "Bitwise and", (x & 1));
}
{
double reference = runningTimes[0] / repeats;
printf("\r\n");
DumpAverage("Plain mod 2", runningTimes[0] / repeats, reference);
DumpAverage("Bitwise or", runningTimes[1] / repeats, reference);
DumpAverage("Bitwise and", runningTimes[2] / repeats, reference);
}
getchar();
return 0;
}
Javaでの答えは次のとおりです。
public static boolean isEven (Integer Number) {
Pattern number = Pattern.compile("^.*?(?:[02]|8|(?:6|4))$");
String num = Number.toString(Number);
Boolean numbr = new Boolean(number.matcher(num).matches());
return numbr.booleanValue();
}
これは単なる構文糖衣であり、.netにのみ適用可能ですが、拡張メソッドについてはどうでしょうか...
public static class RudiGroblerExtensions
{
public static bool IsOdd(this int i)
{
return ((i % 2) != 0);
}
}
これで、次のことができます
int i = 5;
if (i.IsOdd())
{
// Do something...
}
「創造的だが紛らわしいカテゴリ」では、次のことを提案します。
int isOdd(int n) { return n ^ n * n ? isOdd(n * n) : n; }
Microsoft C++ に固有のこのテーマのバリアント:
__declspec(naked) bool __fastcall isOdd(const int x)
{
__asm
{
mov eax,ecx
mul eax
mul eax
mul eax
mul eax
mul eax
mul eax
ret
}
}
IsOdd(int x) { true を返します。}
正しさの証明 - すべての正の整数の集合を考え、奇数ではない空でない整数の集合があるとします。正の整数は適切に順序付けられているため、奇数ではない最小の数が存在します。これはそれ自体がかなり奇数であるため、明らかにその数はセットに含まれません。したがって、このセットを空にすることはできません。奇数ではない最大のものを探すことを除いて、負の整数について繰り返します。
Portable:
i % 2 ? odd : even;
Unportable:
i & 1 ? odd : even;
i << (BITS_PER_INT - 1) ? odd : even;
ビット単位の方法は、整数の内部表現に依存します。モジュロは、モジュロ演算子がある場所ならどこでも機能します。たとえば、システムによっては (動的言語のように) タグ付けに低レベルのビットを実際に使用するため、その場合、生の x & 1 は実際には機能しません。
研究中にブール代数をあまり行わなかった私たちのために、ビットごとの演算子メソッドについてさらに詳しく説明するために、ここで説明します。OPにはあまり役に立たないかもしれませんが、NUMBER & 1が機能する理由を明確にしたいと思いました。
誰かが上で答えたように、負の数を表す方法がこのメソッドの動作を停止する可能性があることに注意してください。実際、各言語は負のオペランドの処理方法が異なる可能性があるため、モジュロ演算子メソッドも壊れる可能性があります。
ただし、NUMBER が常に正であることがわかっている場合、これはうまく機能します。
上記の Tooony が指摘したように、バイナリ (およびデナリ) の最後の桁のみが重要です。
ブール論理 AND ゲートは、1 が返されるには両方の入力が 1 (または高電圧) でなければならないことを示します。
1 & 0 = 0。
0 & 1 = 0。
0 & 0 = 0。
1 & 1 = 1。
数値を 2 進数で表す場合 (ここでは 8 ビット表現を使用しました)、奇数の末尾は 1、偶数の末尾は 0 です。
例えば:
1 = 00000001
2 = 00000010
3 = 00000011
4 = 00000100
任意の数値を使用してビットごとの AND (Java では &) を 1 で使用すると、00000001 が返されます。= 1 は、数値が奇数であることを意味します。または 00000000 = 0 は、数値が偶数であることを意味します。
例えば
変ですか?
1 & 1 =
00000001 &
00000001 =
00000001 <— 奇数
2 & 1 =
00000010 &
00000001 =
00000000 <— 偶数
54 & 1 =
00000001 &
00110110 =
00000000 <— 偶数
これが機能する理由です:
if(number & 1){
//Number is odd
} else {
//Number is even
}
これが冗長である場合は申し訳ありません。
数値ゼロパリティ | ゼロhttp://tinyurl.com/oexhr3k
Python コード シーケンス。
# defining function for number parity check
def parity(number):
"""Parity check function"""
# if number is 0 (zero) return 'Zero neither ODD nor EVEN',
# otherwise number&1, checking last bit, if 0, then EVEN,
# if 1, then ODD.
return (number == 0 and 'Zero neither ODD nor EVEN') \
or (number&1 and 'ODD' or 'EVEN')
# cycle trough numbers from 0 to 13
for number in range(0, 14):
print "{0:>4} : {0:08b} : {1:}".format(number, parity(number))
出力:
0 : 00000000 : Zero neither ODD nor EVEN
1 : 00000001 : ODD
2 : 00000010 : EVEN
3 : 00000011 : ODD
4 : 00000100 : EVEN
5 : 00000101 : ODD
6 : 00000110 : EVEN
7 : 00000111 : ODD
8 : 00001000 : EVEN
9 : 00001001 : ODD
10 : 00001010 : EVEN
11 : 00001011 : ODD
12 : 00001100 : EVEN
13 : 00001101 : ODD
int isOdd(int i){
return(i % 2);
}
終わり。
I execute this code for ODD & EVEN:
#include <stdio.h>
int main()
{
int number;
printf("Enter an integer: ");
scanf("%d", &number);
if(number % 2 == 0)
printf("%d is even.", number);
else
printf("%d is odd.", number);
}
効率的にしたい場合はビット単位の演算子 ( x & 1
) を使用しますが、読みやすくしたい場合は modulo 2 を使用します ( x % 2
)
議論のために...
偶数か奇数かを確認するには、任意の数の最後の桁を見るだけで済みます。符号あり、符号なし、正、負 - これに関してはすべて同じです。したがって、これはすべてのラウンドで機能するはずです: -
void tellMeIfItIsAnOddNumberPlease(int iToTest){
int iLastDigit;
iLastDigit = iToTest - (iToTest / 10 * 10);
if (iLastDigit % 2 == 0){
printf("The number %d is even!\n", iToTest);
} else {
printf("The number %d is odd!\n", iToTest);
}
}
ここで重要なのはコードの 3 行目です。除算演算子は整数除算を実行するため、結果の小数部分が失われます。したがって、たとえば 222 / 10 は結果として 22 を返します。次に、もう一度 10 を掛けると 220 になります。元の 222 からそれを引くと 2 になります。これは魔法により、元の数の最後の桁と同じ数になります。;-) 括弧は、計算が行われる順序を思い出させるためにあります。最初に除算と乗算を行い、次に元の数値から結果を引きます。減算よりも除算と乗算の方が優先度が高いため、それらを除外することもできますが、これにより「より読みやすい」コードが得られます。
必要に応じて、すべてを完全に判読不能にすることもできます。現代のコンパイラにとっては何の違いもありません: -
printf("%d%s\n",iToTest,0==(iToTest-iToTest/10*10)%2?" is even":" is odd");
しかし、将来的にコードを保守するのが難しくなります。奇数のテキストを「偶数ではない」に変更したいと想像してみてください。その後、他の誰かがあなたが行った変更を見つけて、svn diff などを実行したいと考えています...
移植性よりも速度を重視する場合は、最下位ビットを確認できます。そのビットが 1 に設定されている場合は奇数であり、0 の場合は偶数です。Intel の x86 アーキテクチャのようなリトル エンディアン システムでは、次のようになります。
if (iToTest & 1) {
// Even
} else {
// Odd
}
偶数か奇数かのチェックは簡単です。
正確に 2 で割り切れる数は、偶数でなければ奇数であることがわかっています。
任意の数の割り切れる可能性をチェックする必要があるだけであり、割り切れる可能性をチェックするために%
演算子を使用します
if else を使用して偶数奇数をチェックする
if(num%2 ==0)
{
printf("Even");
}
else
{
printf("Odd");
}
if else を使用して偶数または奇数をチェックする C プログラム
条件/三項演算子の使用
(num%2 ==0) printf("Even") : printf("Odd");
条件演算子を使用して偶数または奇数をチェックする C プログラム。
ビット演算子の使用
if(num & 1)
{
printf("Odd");
}
else
{
printf("Even");
}
モジュラス演算子 '%' を使用して、数値が奇数か偶数かを確認できます。つまり、数値を 2 で割り、余りが 0 の場合は偶数、それ以外の場合は奇数です。
#include <stdio.h>
int main()
{
int n;//using modulus operator
scanf("%d",&n);//take input n from STDIN
printf("%s",n%2==0?"Even":"Odd");//prints Even/Odd depending on n to STDOUT
return 0;
}
しかし、ビット操作を使用すると、上記の方法よりもかなり高速になるため、数値を取得してそれに論理 AND '&' を適用すると、答えが 1 の場合は偶数であり、そうでない場合は奇数になります。つまり、基本的に最後のビットをチェックする必要があります。バイナリの数値 n の。最後のビットが 0 の場合、n は偶数であり、それ以外の場合は奇数です。
例: N = 15 と仮定し、2 進数で N = 1111 とすると、1 と AND します。
1111
0001
&-----
0001
結果は 1 なので、N=15 は奇数です。
繰り返しますが、 N = 8 、バイナリで N = 1000 と仮定すると、今度は 1 と AND します
1000
0001
&-----
0000
結果は0なので、N=8は偶数です。
#include <stdio.h>
int main()
{
int n;//using AND operator
scanf("%d",&n);//take input n from STDIN
printf("%s",n&1?"Odd":"Even");//prints Even/Odd depending on n to STDOUT
return 0;
}