Bagaimana cara saya memeriksa apakah bilangan bulat genap atau ganjil? [Tutup]

193

Bagaimana saya bisa memeriksa apakah angka yang diberikan genap atau ganjil dalam C?

chustar
sumber
5
Versi yang menggunakan bitwise dan (&) jauh lebih efisien daripada versi modulo (%). Anda harus mengubah jawaban yang Anda pilih sebagai jawaban yang benar.
Stefan Rusek
6
Tidak mungkin menjadi masalah - argumen adalah konstan. Mudah untuk pengoptimal
MSalters
2
Faktor keterbacaan ke dalam ini juga.
Brian G
2
Dalam aplikasi embedded (dunia tempat saya menghabiskan sebagian besar waktu pemrograman saya), beberapa prosesor memiliki unit aritmatika yang sangat primitif dan tidak dapat melakukan operasi divisi / modulus dengan mudah. Untuk alasan ini, saya biasanya menggunakan metode bitwise-dan sebagai gantinya. Namun, pada CPU desktop modern ini tidak akan terjadi.
bta
3
Saya tidak pernah menemukan operasi modulus lebih mudah dimengerti. Ketika saya pertama kali perlu menentukan genap atau ganjil, topeng bitwise adalah hal pertama yang terlintas di benak saya. Ini agak alami, karena cara kita cenderung melakukan ini dengan tangan adalah dengan melihat angka paling tidak signifikan untuk melihat apakah angka itu ada di {0 2 4 6 8} atau {1 3 5 7 9}. Itu berarti langsung melihat sedikit yang paling signifikan untuk melihat apakah itu 0 atau 1.
P Ayah

Jawaban:

449

Gunakan operator modulo (%) untuk memeriksa apakah ada sisa saat membaginya dengan 2:

if (x % 2) { /* x is odd */ }

Beberapa orang mengkritik jawaban saya di atas yang menyatakan bahwa menggunakan x & 1 adalah "lebih cepat" atau "lebih efisien". Saya tidak percaya ini yang terjadi.

Karena penasaran, saya membuat dua program kasus uji sepele:

/* 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;
}

Saya kemudian mengompilasinya dengan gcc 4.1.3 di salah satu mesin saya 5 kali berbeda:

  • Tanpa flag optimasi.
  • Dengan -O
  • Dengan -Os
  • Dengan -O2
  • Dengan -O3

Saya memeriksa output perakitan dari setiap kompilasi (menggunakan gcc -S) dan menemukan bahwa dalam setiap kasus, output untuk dan.c dan modulo.c adalah identik (mereka berdua menggunakan andl $ 1,% instruksi eax%). Saya ragu ini adalah fitur "baru", dan saya menduga ini berasal dari versi kuno. Saya juga meragukan kompiler non-arcane (komersial dalam 20 tahun terakhir) modern, komersial atau open source, tidak memiliki optimasi seperti itu. Saya akan menguji pada kompiler lain, tetapi saya tidak memiliki yang tersedia saat ini.

Jika ada orang lain yang mau menguji kompiler dan / atau target platform lain, dan mendapatkan hasil yang berbeda, saya akan sangat tertarik untuk mengetahuinya.

Akhirnya, versi modulo dijamin oleh standar untuk bekerja apakah bilangan bulat positif, negatif atau nol, terlepas dari representasi implementasi bilangan bulat yang ditandatangani. Bitwise-dan versinya tidak. Ya, saya menyadari bahwa komplemen dua jenis ini agak ada di mana-mana, jadi ini bukan masalah.

Chris Young
sumber
11
Pertanyaannya secara khusus menanyakan bagaimana melakukannya di C jadi saya menjawabnya di C, meskipun menyebutkan bahwa mereka tidak bisa melakukannya di Jawa. Saya tidak mengklaim atau menyiratkan ini adalah jawaban Java, saya tidak tahu Java. Saya pikir saya baru saja downvote pertama saya dan saya bingung mengapa. Baiklah.
Chris Young
33
Saya akan mengatakan, jika (x% 2! = 0) {/ * x aneh * /}, tetapi siapa tahu. Tidak tahu java juga.
eugensk
9
Semakin banyak upvotes untuk membedakannya dari orang bodoh operator bitwise, tanpa harus menghabiskan karma kami untuk memberikan suara.
Berkumandang
13
Saya setuju dengan semuanya, kecuali satu hal: Saya suka menjaga bilangan bulat dan nilai kebenaran terpisah, secara konseptual, jadi saya lebih suka menulis "jika (x% 2 == 1)". Itu sama dengan kompiler, tapi mungkin sedikit lebih jelas bagi manusia. Plus, Anda dapat menggunakan kode yang sama dalam bahasa yang tidak menafsirkan non-nol sebagai benar.
Thomas Padron-McCarthy
46
Benchmark saya? Benchmark apa? Saya tidak melakukan pembandingan. Saya memeriksa bahasa assembly yang dihasilkan. Ini sama sekali tidak ada hubungannya dengan printf.
Chris Young
207

Kalian waaaaaaaay terlalu efisien. Yang Anda inginkan adalah:

public boolean isOdd(int num) {
  int i = 0;
  boolean odd = false;

  while (i != num) {
    odd = !odd;
    i = i + 1;
  }

  return odd;
}

Ulangi untuk isEven.

Tentu saja, itu tidak berfungsi untuk angka negatif. Tetapi dengan kecemerlangan datang pengorbanan ...

SCdF
sumber
17
Jika Anda melemparkan pengecualian argumen pada nilai negatif, dan mencatat dalam dokumentasi bahwa fungsi ini adalah O (N), maka saya akan setuju dengan ini.
Jeffrey L Whitledge
7
Versi perusahaan harus menggunakan XML. Tentu saja saat ini Anda akan memiliki layanan web yang dapat Anda tanyakan
Martin Beckett
58
Anda harus mengoptimalkan ini dengan tabel pencarian.
Weeble
1
Saya seorang bhikkhu, harus memberi +1 6.999 perwakilan Anda ke milenium baru
Eran Medan
7
Ini brilian! Bos saya memberi tahu saya bahwa kami memiliki klien yang marah karena dia merasa Lisensi Perusahaannya tidak memberikan apa pun selain Lisensi Standar. Sekarang kami telah menambahkan fungsi ini dalam program kami, dan hanya karena dijalankan lebih lambat, ia pikir perangkat lunaknya melakukan CARA lebih banyak pekerjaan !!!
Phil
97

Gunakan bit aritmatika:

if((x & 1) == 0)
    printf("EVEN!\n");
else
    printf("ODD!\n");

Ini lebih cepat daripada menggunakan pembagian atau modulus.

Adam Pierce
sumber
43
Saya tidak berpikir itu adil untuk mengatakan itu lebih cepat daripada menggunakan pembagian atau modulus. Standar C tidak mengatakan apa pun tentang kinerja operator, dan penyusun yang layak apa pun akan menghasilkan kode cepat untuk keduanya. Saya pribadi akan memilih idiom yang mengomunikasikan maksud saya, dan% tampaknya lebih sesuai di sini
Chris Young
21
Saya suka (x & 1) lebih baik, karena memeriksa apakah nomornya bahkan sama dengan yang dilakukan orang: memeriksa apakah angka terakhir genap atau ganjil. Menurut pendapat saya itu mengomunikasikan maksudnya lebih dari metode modulo. (Bukan berarti itu penting.)
Jeremy Ruten
2
Anda benar, saya kira itu subjektif. Meskipun definisi "bahkan" yang umum adalah "integer yang dapat dibagi dengan 2", bukan "integer yang berakhir dengan 0, 2, 4, 6 atau 8". :-)
Chris Young
4
@TraumaPony - untuk standar ANSI C dan Java awal, tergantung pada sistem komputer. Tidak ditentukan representasi apa yang digunakan untuk angka yang ditandatangani - pujian 2, pujian 1, kode abu-abu, dll. Tapi modulus selalu modulus
Aaron
9
Tidak bekerja secara universal untuk angka negatif. Lihat Periksa jawaban ini untuk detail lebih lanjut: stackoverflow.com/questions/160930/… untuk detailnya.
Andrew Edgecombe
36

[Mode Joke = "on"]

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;
  }
}

[Mode Lelucon = "mati"]

EDIT: Menambahkan nilai membingungkan ke enum.

Sklivvz
sumber
2
Wow ... ini lebih gila daripada solusi SCdF! Pujian! Tidak ada suara positif ... tidak dapat merekomendasikan ini. Tapi terima kasih untuk yang lucu!
Wes P
1
Keuntungan dari pendekatan ini adalah ia bekerja dengan lebih dari sekedar angka. Juga, jika Anda mengganti baris ini: char bar = foo [foo.Length - 1]; dengan ini: double bar = Char.GetNumericValue (foo [foo.Length - 1]); Maka itu akan bekerja dengan sistem nomor apa pun.
Jeffrey L Whitledge
5
laporan bug: 14,65 dilaporkan aneh ketika seharusnya tidak diketahui.
TheSoftwareJedi
4
Perangkat lunak Jedi, ini adalah "fitur". ;)
Sklivvz
31
TheSoftwareJedi: 14.65 adalah salah satu bilangan bulat paling aneh yang pernah saya lihat.
Bruce Alderman
16

Menanggapi ffpf - saya memiliki argumen yang persis sama dengan seorang rekan bertahun-tahun yang lalu, dan jawabannya adalah tidak , itu tidak bekerja dengan angka negatif.

Standar C menetapkan bahwa angka negatif dapat direpresentasikan dalam 3 cara:

  • Pelengkap 2
  • Pelengkap 1
  • tanda dan besarnya

Memeriksa seperti ini:

isEven = (x & 1);

akan bekerja untuk komplemen 2 dan tanda dan representasi besarnya, tetapi tidak untuk komplemen 1.

Namun, saya percaya bahwa yang berikut ini akan berfungsi untuk semua kasus:

isEven = (x & 1) ^ ((-1 & 1) | ((x < 0) ? 0 : 1)));

Terima kasih kepada ffpf karena menunjukkan bahwa kotak teks memakan segalanya setelah karakter saya yang kurang!

Andrew Edgecombe
sumber
Saya pikir contoh kode kedua Anda kehilangan beberapa teks.
Jeff Yates
3
Mari kita memuji angka-angka itu!
thejh
14

Yang bagus adalah:

/*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
}

Perhatikan bahwa metode ini menggunakan rekursi ekor yang melibatkan dua fungsi. Ini dapat diimplementasikan secara efisien (berubah menjadi semacam sementara / sampai loop) jika kompiler Anda mendukung rekursi ekor seperti kompiler Skema. Dalam hal ini tumpukan tidak boleh meluap!

Pierre
sumber
1
Ini tidak menangani isOdd (0) dengan baik.
Steve McLeod
1
Saya pikir Anda punya loop tak terbatas (dengan rekursi ekor) atau stack overflow (tanpa rekursi ekor) untuk isOdd () dengan nilai genap atau isEven () dengan nilai ganjil. Itu hanya berakhir dengan true. Ini masalah penghentian lagi.
Jeffrey L Whitledge
7
Oh, tentu, perbaiki tanpa komentar, dan buat aku terlihat seperti orang idiot. Tidak apa-apa.
Jeffrey L Whitledge
1
Sekarang, Anda memiliki kesalahan kompilasi: di isEven tidak semua jalur kode mengembalikan nilai. Tidak, saya belum benar-benar mencoba kode ini, itu kompiler di kepala saya yang mengeluh.
Jeffrey L Whitledge
5
kompilasi kesalahan: tidak semua jalur mengembalikan nilai benci untuk membombardir Anda dengan komentar bug pada kode sampel Anda, tetapi apa yang terjadi ketika Anda memanggil isEven (5)
Kevin
11

Angka adalah bahkan jika, ketika dibagi dua, sisanya adalah 0. Angka ganjil jika, ketika dibagi 2, sisanya adalah 1.

// Java
public static boolean isOdd(int num){
    return num % 2 != 0;
}

/* C */
int isOdd(int num){
    return num % 2;
}

Metode sangat bagus!

jjnguy
sumber
Metode Java Anda rusak karena num% 2 == -1 untuk angka ganjil negatif.
WMR
Apakah itu sebabnya Anda menurunkan saya?
jjnguy
3
Saya menurunkannya karena fungsi Anda di C membutuhkan lebih banyak karakter untuk diketik daripada apa yang dilakukannya. IE num% I adalah 7 karakter termasuk spasi IsOdd (I) adalah 8 karakter. Mengapa Anda membuat fungsi yang lebih lama dari sekadar melakukan operasi?
Kevin
13
@Kevin dalam kode pendapat saya tidak diukur dengan karakter melainkan pada saat Anda menulisnya, termasuk waktu think + debug. num% 2 membutuhkan milidetik lebih untuk dipikirkan daripada isOdd. sekarang tambahkan angka secara global dan Anda kehilangan tahun kolektif. juga isOdd dapat diuji, dan diverifikasi dan akhirnya disertifikasi bebas bug (mis. menangani angka negatif) di mana num% 2 - beberapa pengembang akan selalu ragu dan bereksperimen. kode yang baik adalah kode yang tidak Anda tulis, gunakan kembali ... hanya 2 sen saya.
Eran Medan
2
@EranMedan, Logika yang sama akan berlaku untuk mengganti i ++ dengan IncrementByOne (i) dan itu adalah ide yang sama buruknya. Jika pengembang ragu tentang apa yang dilakukan angka 2, saya tidak ingin dia mendekati kode saya.
Kevin
8
i % 2 == 0
Mark Cidade
sumber
7

Saya akan mengatakan hanya membaginya dengan 2 dan jika ada sisa 0, itu genap, kalau tidak aneh.

Menggunakan modulus (%) membuatnya mudah.

misalnya. 4% 2 = 0 karena itu 4 bahkan 5% 2 = 1 oleh karena itu 5 aneh

Jarod Elliott
sumber
6

Satu lagi solusi untuk masalah ini
(anak-anak boleh memilih)

bool isEven(unsigned int x)
{
  unsigned int half1 = 0, half2 = 0;
  while (x)
  {
     if (x) { half1++; x--; }
     if (x) { half2++; x--; }

  }
  return half1 == half2;
}
eugensk
sumber
Tidak, Anda bukan tipe anak yang saya
andalkan
Saya akan membatalkan ini, tapi agak lambat pada angka negatif. :)
Chris Young
3
Semua angka cerah dan positif. Atau apakah Anda berprasangka terhadap beberapa orang? :))
eugensk
3
Di komputer, semua angka yang dulu negatif, akhirnya menjadi positif. Kami menyebutnya Rollover of Happiness (tidak berlaku untuk BIGNUMS, YMMY, tidak berlaku di semua negara bagian).
Will Hartung
@WillHartung "rollover of happiness" is great! : D
thejh
6

Saya akan membangun tabel paritas (0 jika bahkan 1 jika ganjil) dari bilangan bulat (sehingga orang dapat melakukan pencarian: D), tetapi gcc tidak akan membiarkan saya membuat array dengan ukuran seperti ini:

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];
}

Jadi mari kita beralih ke definisi matematika genap dan gantinya.

Integer n adalah bahkan jika ada integer k sehingga n = 2k.

Suatu bilangan bulat n ganjil jika ada bilangan bulat k sedemikian sehingga n = 2k + 1.

Ini kode untuk itu:

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;
}

Biarkan C-integer menunjukkan nilai yang mungkin dari intdalam kompilasi C yang diberikan. (Perhatikan bahwa C-integer adalah subset dari integer.)

Sekarang orang mungkin khawatir bahwa untuk n yang diberikan dalam C-integer bahwa k integer yang sesuai mungkin tidak ada dalam C-integer. Tetapi dengan sedikit bukti dapat ditunjukkan bahwa untuk semua bilangan bulat n, | n | <= | 2n | (*), di mana | n | adalah "n jika n positif dan -n sebaliknya". Dengan kata lain, untuk semua n dalam bilangan bulat setidaknya salah satu dari pegangan berikut (tepatnya case (1 dan 2) atau case (3 dan 4) sebenarnya tetapi saya tidak akan membuktikannya di sini):

Kasus 1: n <= 2n.

Kasus 2: -n <= -2n.

Kasus 3: -n <= 2n.

Kasus 4: n <= -2n.

Sekarang ambil 2k = n. (Ak semacam itu memang ada jika n adalah genap, tetapi saya tidak akan membuktikannya di sini. Jika n tidak genap maka loop ineven gagal kembali lebih awal, jadi itu tidak masalah.) Tapi ini menyiratkan k <n jika n bukan 0 oleh (*) dan fakta (sekali lagi tidak terbukti di sini) bahwa untuk semua m, z dalam bilangan bulat 2m = z menyiratkan z tidak sama dengan m yang diberikan m bukan 0. Dalam kasus n adalah 0, 2 * 0 = 0 jadi 0 bahkan kita sudah selesai (jika n = 0 maka 0 ada di C-integer karena n ada di C-integer dalam fungsi even, maka k = 0 ada di C-integer). Dengan demikian ak dalam C-integer ada untuk n dalam C-integer jika n adalah genap.

Argumen serupa menunjukkan bahwa jika n ganjil, ada ak di C-integer sehingga n = 2k + 1.

Karenanya fungsi evendan yang odddisajikan di sini akan berfungsi dengan baik untuk semua C-integer.

Thomas Eding
sumber
1
Maksud saya bukan pelanggaran, tetapi apa gunanya jawaban ini? i % 2jauh lebih kecil dan mungkin lebih efisien.
GManNickG
2
@ GM: Tapi ini jauh lebih deterministik! Ini akan bekerja dengan benar mendeteksi semua tepi kasus.
P Ayah
1
... DAN (!!!) itu benar !!!
Thomas Eding
Saya tidak tahu apakah Anda bercanda atau tidak. : X %2bekerja untuk semua bilangan bulat.
GManNickG
1
+1: Saya akan mengatakan "Jawaban Baik", tapi saya pikir "Jawaban Menarik" lebih tepat.
James Webster
5
// C#
bool isEven = ((i % 2) == 0);
Michael Petrotta
sumber
2
Apa? Itu bukan C #! Itu murni C! :-P
asterite
8
Saya akan melempar WinForm di sekitarnya untuk menjadikannya murni C # ...
Michael Petrotta
@mateusza: Biasanya ketika Anda melihat "bool" di beberapa huruf besar atau lainnya di C, itu adalah typedefatau #defineatau sesuatu.
David Thornley
2
@mateusza @ David Thornley In C99 bool adalah fitur standar ( en.wikipedia.org/wiki/Stdbool.h )
fortran
1
Bicara tentang kurung yang sangat berlebihan ...
Thomas Eding
4

Ini jawaban di Jawa:

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();
}
Thomas Eding
sumber
4

Coba ini: return (((a>>1)<<1) == a)

Contoh:

a     =  10101011
-----------------
a>>1 --> 01010101
a<<1 --> 10101010

b     =  10011100
-----------------
b>>1 --> 01001110
b<<1 --> 10011100
Kiril Aleksandrov
sumber
Bisakah Anda jelaskan ini? Saya sangat tidak terbiasa dengan operator bitwise
Abdul
Bergeser ke kanan dan ke kiri akan nol bit terakhir Anda (yang paling kanan). Jika nomor baru sama dengan yang asli, ini berarti bahwa bit terakhir dari angka asli adalah 0. Jadi itu genap. Lihatlah jawaban saya yang diperbarui.
Kiril Aleksandrov
terima kasih, saya mengerti sekarang
Abdul
Saya tidak yakin pendekatan mana yang lebih cepat. Saya belum mencoba membandingkan mereka.
Kiril Aleksandrov
Bukankah ini juga nol bit paling signifikan Anda? Masalah dengan int unsigned dalam beberapa bahasa dan int negatif di sebagian besar ...
Troyseph
4

Membaca diskusi yang agak menghibur ini, saya ingat bahwa saya memiliki dunia nyata, fungsi sensitif waktu yang menguji angka ganjil dan genap di dalam lingkaran utama. Ini adalah fungsi daya integer, diposting di tempat lain di StackOverflow, sebagai berikut. Tolok ukurnya cukup mengejutkan. Setidaknya dalam fungsi dunia nyata ini, modulo lebih lambat , dan sangat signifikan. Pemenang, dengan selisih yang lebar, membutuhkan 67% waktu modulo, adalah pendekatan atau (|) , dan tidak ditemukan di mana pun di halaman ini.

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;
}

Untuk 300 juta loop, timing benchmark adalah sebagai berikut.

3.962 | dan pendekatan topeng

4.851 pendekatan &

5.850 pendekatan%

Untuk orang-orang yang berpikir teori, atau daftar bahasa majelis, menyelesaikan argumen seperti ini, ini harus menjadi kisah peringatan. Ada lebih banyak hal di surga dan bumi, Horatio, daripada yang diimpikan dalam filosofi Anda.


sumber
1
Lebih baik digunakan unsigned xsebagaimana x = x >> 1;implementasi-didefinisikan perilaku saat x < 0. Belum jelas alasan xdan OrMaskjenisnya berbeda. Cukup sederhana untuk menulis ulang menggunakan while(x)tes.
chux
2
Saya ingin tahu kompiler mana yang Anda gunakan untuk benchmark ini, karena sebagian besar kompiler harus cukup pintar untuk mengkompilasi % 2kasing menggunakan bitwise &. Saya baru saja menguji ini dan hasilnya benar-benar sama (VS2015, Rilis dibangun dengan semua optimisasi, baik x86 dan x64). Jawaban yang diterima juga menyatakan ini untuk GCC (ditulis pada 2008).
Lou
2
Masalah dengan posting ini adalah bahwa premis bahwa bitwise orakan lebih cepat daripada andsangat tidak mungkin, pada platform / kompiler. Bahkan jika ada kombinasi platform / kompiler yang aneh (dan Anda belum mempostingnya atau kode yang digunakan untuk melakukan benchmark), tergantung pada kompiler lain untuk berperilaku yang sama akan menjadi taruhan optimasi yang buruk. Jadi, ketika saya menulis, saya bertanya-tanya di platform / kompiler mana ini diuji , karena saya hampir yakin itu tidak diukur dengan benar.
Lou
2
Tidak menyebut Anda pembohong, hanya mengklaim dengan kepastian tinggi bahwa Anda tidak mengukur dengan benar. Tidak perlu memanggil saya seorang pengemudi truk, baca komentar asli saya: Saya memang membuat patokan, dan hasilnya, seperti yang diharapkan, sepenuhnya sama dalam ketiga kasus (kepastian ~ 3 sigma, setelah menjalankan setiap tes 10 kali untuk 500.000 .000 iterasi). Jika Anda benar-benar memiliki karir yang terkenal, mundur selangkah dan berpikir jika klaim Anda masuk akal, maka poskan kode aktual yang digunakan untuk melakukan tolok ukur. Kalau tidak, posting itu adalah apa yang saya yakini, hanya kesalahan dalam pengukuran.
Lou
1
Selesai .
Lou
4

Ini adalah tindak lanjut dari diskusi dengan @RocketRoy mengenai jawabannya , tetapi mungkin bermanfaat bagi siapa saja yang ingin membandingkan hasil ini.

tl; dr Dari apa yang saya lihat, pendekatan Roy ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) tidak sepenuhnya dioptimalkan x & 1sebagai modpendekatan, tetapi dalam praktiknya waktu berjalan seharusnya menjadi sama dalam semua kasus.

Jadi, pertama saya membandingkan output yang dikompilasi menggunakan Compiler Explorer :

Fungsi yang diuji:

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));
}   

Klik 3.9.0 dengan -O3:

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

GCC 6.2 dengan -O3:

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

Angkat ke CLang, itu menyadari bahwa ketiga kasus secara fungsional sama. Namun, pendekatan Roy tidak dioptimalkan dalam GCC, begitu YMMV.

Ini mirip dengan Visual Studio; memeriksa Rilis pembongkaran x64 (VS2015) untuk tiga fungsi ini, saya bisa melihat bahwa bagian perbandingannya sama untuk case "mod" dan "dan", dan sedikit lebih besar untuk case Roy "atau":

// 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)

Namun, setelah menjalankan tolok ukur yang sebenarnya untuk membandingkan tiga opsi ini (mod sederhana, bitwise atau, bitwise dan), hasilnya benar-benar sama (sekali lagi, Visual Studio 2005 x86 / x64, rilis Rilis, tidak ada debugger terpasang).

Majelis rilis menggunakan testinstruksi untuk anddan modkasus, sedangkan kasus Roy menggunakancmp eax,0FFFFFFFFh pendekatan, tetapi sangat terbuka dan dioptimalkan sehingga tidak ada perbedaan dalam prakteknya.

Hasil saya setelah 20 berjalan (i7 3610QM, rencana daya Windows 10 diatur ke Kinerja Tinggi):

[Tes: Plain mod 2] WAKTU RATA-RATA: 689,29 ms (Perbedaan relatif .: + 0,000%)
[Tes: Bitwise atau] WAKTU RATA-RATA: 689,63 ms (Perbedaan relatif .: + 0,048%)
[Tes: Bitwise dan] WAKTU RATA-RATA: 687,80 ms (Perbedaan relatif .: -0,217%)

Perbedaan antara opsi-opsi ini kurang dari 0,3%, jadi agak jelas rakitannya sama dalam semua kasus.

Berikut adalah kode jika ada yang ingin mencoba, dengan peringatan bahwa saya hanya mengujinya pada Windows (periksa #if LINUXpersyaratan untuk get_timedefinisi dan menerapkannya jika perlu, diambil dari jawaban ini ).

#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;
}
Lou
sumber
Saya percaya Anda telah melakukan dosa pembandingan dari Kardinal; membuat satu yang begitu spesifik itu tidak mewakili lingkungan dunia nyata. Lihatlah bahasa majelis Anda dan perhatikan sedikitnya register yang Anda gunakan. Nilai tinggi untuk usaha, tetapi hasil ini tidak akan bertahan dalam pemrosesan dunia nyata.
@RocketRoy: karena semua output persis sama untuk ketiga kasus (well, sedikit lebih buruk untuk program Anda dalam satu kasus), saya tidak terlalu peduli berapa banyak register yang digunakan. Tetapi sekali lagi, jangan ragu untuk membuat dan memposting contoh program / lingkungan yang akan membingungkan kompiler untuk membuat perakitan yang lebih optimal dalam salah satu kasus, semua hal lain dianggap sama.
Lou
Saya selalu suka programmer sombong. Ini adalah sifat yang baik untuk dimiliki seorang programmer, tetapi dalam program dunia nyata yang lebih kompleks, metode saya akan berkinerja lebih baik daripada Anda karena kompiler memiliki lebih banyak cara untuk menyelesaikan masalah sehingga instruksi yang tumpang tindih (pada arsitektur Intel) menghasilkan hasil yang lebih baik . Sangat sedikit programmer veteran dengan pengalaman benchmark yang baik akan lebih suka benchmark Anda, tetapi tetap bekerja dengan baik, dan ingat untuk menjalankan kembali benchmark Anda ketika rilis chip baru keluar. Banyak hal berubah seiring waktu.
3

Saya tahu ini hanya gula sintaksis dan hanya berlaku di .net tetapi bagaimana dengan metode ekstensi ...

public static class RudiGroblerExtensions
{
    public static bool IsOdd(this int i)
    {
        return ((i % 2) != 0);
    }
}

Sekarang Anda dapat melakukan hal berikut

int i = 5;
if (i.IsOdd())
{
    // Do something...
}
rudigrobler
sumber
1
Kode yang bagus Sayang sekali bahwa itu akan mengklaim bahwa 2 itu aneh, dan 3 tidak.
Anthony
oops, maaf ... logikanya salah ...
rudigrobler
3

Dalam "kategori kreatif tapi membingungkan" saya menawarkan:

int isOdd(int n) { return n ^ n * n ? isOdd(n * n) : n; }

Varian pada tema ini yang khusus untuk 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
    }
}
DocMax
sumber
2

Metode bitwise tergantung pada representasi bagian dalam bilangan bulat. Modulo akan bekerja di mana saja ada operator modulo. Sebagai contoh, beberapa sistem benar-benar menggunakan bit level rendah untuk penandaan (seperti bahasa dinamis), sehingga x & 1 mentah tidak akan benar-benar berfungsi dalam kasus itu.

Will Hartung
sumber
2

IsOdd (int x) {return true; }

Bukti kebenaran - pertimbangkan himpunan semua bilangan bulat positif dan anggap ada satu set bilangan bulat yang tidak kosong yang tidak aneh. Karena bilangan bulat positif disusun dengan baik, akan ada bilangan terkecil bukan bilangan ganjil, yang dengan sendirinya cukup aneh, sehingga jelas bilangan itu tidak dapat diatur. Karenanya set ini tidak boleh kosong. Ulangi untuk bilangan bulat negatif kecuali mencari bilangan terbesar bukan ganjil.

alas tiang
sumber
2

Portabel:

i % 2 ? odd : even;

Tidak dapat diangkut:

i & 1 ? odd : even;

i << (BITS_PER_INT - 1) ? odd : even;
ilitirit
sumber
2

Seperti yang telah diposting beberapa orang, ada banyak cara untuk melakukan ini. Menurut situs web ini , cara tercepat adalah operator modulus:

if (x % 2 == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

Namun, berikut adalah beberapa kode lain yang ditandai oleh penulis yang berjalan lebih lambat daripada operasi modulus umum di atas:

if ((x & 1) == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

System.Math.DivRem((long)x, (long)2, out outvalue);
        if ( outvalue == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x / 2) * 2) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x >> 1) << 1) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

        while (index > 1)
               index -= 2;
        if (index == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

tempstr = x.ToString();
        index = tempstr.Length - 1;
        //this assumes base 10
        if (tempstr[index] == '0' || tempstr[index] == '2' || tempstr[index] == '4' || tempstr[index] == '6' || tempstr[index] == '8')
               total += 1; //even number
        else
               total -= 1; //odd number

Berapa banyak orang yang tahu tentang metode Math.System.DivRem atau mengapa mereka menggunakannya ??


sumber
1
int isOdd(int i){
  return(i % 2);
}

selesai

Tidak ada
sumber
Lelucon Anda memberikan jawaban yang salah.
Thomas Eding
1

Untuk memberikan elaborasi lebih lanjut tentang metode operator bitwise bagi kita yang tidak melakukan banyak aljabar boolean selama studi kami, berikut adalah penjelasannya. Mungkin tidak banyak berguna untuk OP, tapi saya ingin menjelaskan mengapa NUMBER & 1 berfungsi.

Harap perhatikan seperti ketika seseorang menjawab di atas, cara angka negatif ditampilkan dapat menghentikan metode ini berfungsi. Bahkan ia dapat merusak metode operator modulo juga karena masing-masing bahasa dapat berbeda dalam bagaimana berurusan dengan operan negatif.

Namun jika Anda tahu bahwa NUMBER akan selalu positif, ini berfungsi dengan baik.

Seperti Tooony di atas menyatakan bahwa hanya digit terakhir dalam biner (dan denary) yang penting.

Logika AND gerbang boolean menentukan bahwa kedua input harus berupa 1 (atau tegangan tinggi) agar 1 dikembalikan.

1 & 0 = 0.

0 & 1 = 0.

0 & 0 = 0.

1 & 1 = 1.

Jika Anda mewakili angka apa pun sebagai biner (saya telah menggunakan representasi 8 bit di sini), angka ganjil memiliki 1 di akhir, bahkan angka memiliki 0.

Sebagai contoh:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Jika Anda mengambil nomor apa pun dan menggunakan bitwise AND (& dalam java) dengan 1 maka akan mengembalikan 00000001, = 1 yang berarti angkanya ganjil. Atau 00000000 = 0, artinya angkanya genap.

Misalnya

Aneh?

1 & 1 =

00000001 &

00000001 =

00000001 <- Ganjil

2 & 1 =

00000010 &

00000001 =

00000000 <- Merata

54 & 1 =

00000001 &

00110110 =

00000000 <- Merata

Inilah mengapa ini bekerja:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Maaf jika ini berlebihan.

Astridax
sumber
1

Angka nol paritas | nol http://tinyurl.com/oexhr3k

Urutan kode 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))

Keluaran:

   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

sumber
@ el.pescado, Terima Kasih. Jika Nol genap, berapa pasangan yang dimilikinya?
@ el.pescado, Ok, saya setuju dengan Anda. Lalu, Kalau dipikir-pikir, kenapa kita bagi 2 (dua)? Apa yang ingin kita ketahui, ketika kita bagi dua? Mengapa tidak membagi menjadi 3, atau, 5, dll.?
@ el.pescado Artikel Wikipedia ini Parity of Zero salah. Banyak orang tertipu oleh artikel ini. Pikirkan sebelum Wink.
1
Kamu benar. Sekarang saya sudah membaca jawaban lain saya menemukan jawaban Anda yang paling komprehensif :)
el.pescado
@ el.pescado. Terima kasih. :) Sekarang teman terbaik You're Zero. (pelukan)
1
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);
}
Omar Faruk
sumber
0

Demi diskusi ...

Anda hanya perlu melihat angka terakhir di nomor mana saja untuk melihat apakah angka itu genap atau ganjil. Ditandatangani, tidak ditandatangani, positif, negatif - semuanya sama dalam hal ini. Jadi ini harus bekerja sepanjang: -

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);
  }
}

Kuncinya di sini adalah di baris ketiga kode, operator divisi melakukan divisi integer, sehingga hasilnya hilang bagian fraksi dari hasil. Jadi misalnya 222/10 akan memberikan 22 hasilnya. Kemudian kalikan lagi dengan 10 dan Anda memiliki 220. Kurangi itu dari yang asli 222 dan Anda berakhir dengan 2, yang dengan sihir adalah angka yang sama dengan angka terakhir di angka asli. ;-) Tanda kurung ada di sana untuk mengingatkan kita tentang urutan perhitungan yang dilakukan. Pertama lakukan pembagian dan perkaliannya, kemudian kurangi hasilnya dari angka aslinya. Kita dapat mengabaikannya, karena prioritas lebih tinggi untuk pembagian dan penggandaan daripada pengurangan, tetapi ini memberi kita kode "lebih mudah dibaca".

Kita bisa membuat semuanya benar-benar tidak dapat dibaca jika kita mau. Tidak ada bedanya untuk kompiler modern: -

printf("%d%s\n",iToTest,0==(iToTest-iToTest/10*10)%2?" is even":" is odd");

Tapi itu akan membuat kode lebih sulit untuk dipertahankan di masa depan. Bayangkan saja Anda ingin mengubah teks untuk angka ganjil menjadi "tidak genap". Kemudian orang lain di kemudian hari ingin mengetahui perubahan apa yang Anda buat dan melakukan svn diff atau yang serupa ...

Jika Anda tidak khawatir tentang portabilitas tetapi lebih banyak tentang kecepatan, Anda bisa melihat sedikit yang paling signifikan. Jika bit itu diatur ke 1 itu adalah angka ganjil, jika itu 0 itu adalah angka genap. Pada sistem endian kecil, seperti arsitektur x86 Intel akan menjadi seperti ini: -

if (iToTest & 1) {
  // Even
} else {
  // Odd
}
Tooony
sumber
Apa sebenarnya yang salah dengan hanya menjalankan iToTest% 2 == 0? Anda menyia-nyiakan divisi yang mengekstraksi digit terakhir, jadi Anda dua kali lebih lambat dari yang seharusnya.
freespace
@freespace: Saya membuang lebih dari itu, bukan? :-) Penggandaan dan pengurangan juga. Tapi apa yang paling efisien di antara dua solusi yang saya tidak berani katakan. Tidak pernah mengklaim ini sebagai solusi tercepat, justru sebaliknya jika Anda membaca baris pertama posting saya lagi.
Tooony
@Tonyony, ah, topi humor saya jatuh. Ini secara resmi kembali sekarang: D Maaf tentang itu :)
freespace
0

Jika Anda ingin menjadi efisien, gunakan operator bitwise ( x & 1), tetapi jika Anda ingin dibaca, gunakan modulo 2 ( x % 2)

Vihung
sumber
-1: Jika Anda ingin menjadi efisien, gunakan salah satunya. Jika Anda ingin portabel, gunakan %. Jika Anda ingin dapat dibaca, gunakan %. Hmmm, saya melihat polanya di sini.
Thomas Eding
@trinithis, tidak ada pola dan solusi ini jauh lebih baik daripada milik Anda.
Subs
0

Memeriksa genap atau ganjil adalah tugas sederhana.

Kita tahu bahwa angka apa pun yang dapat habis dibagi 2 adalah angka genap.

Kami hanya perlu memeriksa pembagian nomor apa pun dan untuk memeriksa pembagian, kami menggunakan %operator

Memeriksa bahkan aneh menggunakan jika lain

if(num%2 ==0)  
{
    printf("Even");
}
else
{
    printf("Odd");
}

Program C untuk memeriksa genap atau ganjil menggunakan jika lain

Menggunakan operator Conditional / Ternary

(num%2 ==0) printf("Even") : printf("Odd");

Program C untuk memeriksa genap atau ganjil menggunakan operator kondisional .

Menggunakan operator Bitwise

if(num & 1)  
{
    printf("Odd");
}
else 
{
    printf("Even");
}
Pankaj Prakash
sumber
dan di mana tepatnya operator ternary?
Beyondo
0

+ 66% lebih cepat>!(i%2) / i%2 == 0

int isOdd(int n)
{
    return n & 1;
}

Kode memeriksa bit terakhir dari integer jika 1 dalam Binary

Penjelasan

Binary  :   Decimal
-------------------
0000    =   0
0001    =   1
0010    =   2
0011    =   3
0100    =   4
0101    =   5
0110    =   6
0111    =   7
1000    =   8
1001    =   9
and so on...

Perhatikan bit paling kanan selalu 1 untuk nomor Ganjil .

yang & bitwise operator AND cek bit paling kanan di kami kembali baris jika itu 1

Anggap itu benar & salah

Ketika kita membandingkan n dengan 1 yang berarti 0001dalam biner (jumlah nol tidak masalah).
maka mari kita bayangkan saja kita memiliki bilangan bulat n dengan ukuran 1 byte.

Itu akan diwakili oleh 8-bit / 8-binary digit.

Jika int n adalah 7 dan kami membandingkannya dengan 1 , Ini seperti

7 (1-byte int)|    0  0  0  0    0  1  1  1
       &
1 (1-byte int)|    0  0  0  0    0  0  0  1
********************************************
Result        |    F  F  F  F    F  F  F  T

Yang F adalah false dan T untuk true.

Itu hanya membandingkan bit paling kanan jika keduanya benar. Jadi, secara otomatis 7 & 1adalah T rue.

Bagaimana jika saya ingin memeriksa bit sebelum yang paling kanan?

Hanya mengubah n & 1ke n & 2yang 2 mewakili 0010di Binary dan sebagainya.

Saya sarankan menggunakan notasi heksadesimal jika Anda seorang pemula untuk operasi bitwise
return n & 1;>> return n & 0x01;.

Beyondo
sumber