Bir tamsayının çift mi yoksa tek mi olduğunu nasıl kontrol edebilirim? [kapalı]


193

C'de verilen bir sayının çift mi yoksa tek mi olduğunu nasıl kontrol edebilirim?


5
Bitsel ve (&) kullanan sürüm, modulo (%) sürümüne göre çok daha verimlidir. Seçtiğinizi doğru cevap olarak değiştirmeniz gerekir.
Stefan Rusek

6
Önemli değil - argüman sabittir. Optimizer için kolay
MSalters

2
Okunabilirlik de buna dahil.
Brian G

2
Gömülü uygulamalarda (programlama süremin çoğunu harcadığım dünya), bazı işlemciler çok ilkel aritmetik birimlere sahiptir ve bölme / modül işlemlerini kolayca yapamazlar. Bu nedenle, bunun yerine genellikle bitsel-ve yöntemini kullanıyorum. Ancak, modern bir masaüstünün CPU'sunda durum böyle olmayacak.
bta

3
Modül işlemini hiç bu kadar kolay olmamıştı. Çift veya tek belirlemem gerektiğinde, ilk akla gelen bitsel maske oldu. Biraz doğaldır, çünkü bunu elle yapma eğilimimiz, {0 2 4 6 8} veya {1 3 5 7 9} içinde olup olmadığını görmek için en az önemli basamağa bakmaktır. Bu doğrudan 0 ya da 1 olup olmadığını görmek için en az önemli biti bakmak anlamına gelir.
P Daddy

Yanıtlar:


449

2'ye bölerken kalanların olup olmadığını kontrol etmek için modulo (%) operatörünü kullanın:

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

Birkaç kişi cevabımı x & 1 kullanmanın "daha hızlı" veya "daha verimli" olduğunu belirterek eleştirdi. Bunun böyle olduğuna inanmıyorum.

Meraktan, iki önemsiz test vaka programı oluşturdum:

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

Daha sonra bunları makinelerimden birinde 5 farklı kez gcc 4.1.3 ile derledim:

  • Hiçbir optimizasyon bayrağı olmadan.
  • -O ile
  • -Os ile
  • -O2 ile
  • -O3 ile

Her derlemenin derleme çıktısını inceledim (gcc -S kullanarak) ve her durumda and.c ve modulo.c çıktılarının aynı olduğunu gördüm (her ikisi de andl $ 1,% eax komutunu kullandılar). Bunun "yeni" bir özellik olduğundan şüpheliyim ve eski sürümlere dayantığından şüpheleniyorum. Ayrıca herhangi bir modern (son 20 yılda yapılan) gizli olmayan derleyici, ticari veya açık kaynaklı, bu tür bir optimizasyondan yoksundur. Diğer derleyicileri test ederdim ama şu anda elimde hiç yok.

Başka herhangi bir derleyici ve / veya platform hedefini test etmek ister ve farklı bir sonuç alırsa, bilmek isterim.

Son olarak, modulo sürümü, uygulamanın imzalanmış tamsayıları temsil etmesine bakılmaksızın, tamsayı pozitif, negatif veya sıfır olsun, standart tarafından çalışacağı garanti edilir. Bitsel ve sürümü değildir. Evet, ikisinin tamamlayıcının biraz yaygın olduğunu fark ediyorum, bu yüzden bu gerçekten bir sorun değil.


11
Soru özellikle C'de nasıl yapılacağını sordu, bu yüzden Chustar'ın Java'da nasıl yapılacağını anlayamadıklarından bahsetmesine rağmen C'de cevapladım. Bu bir Java cevabı olduğunu iddia etmedim veya ima etmedim, Java bilmiyorum. Sanırım ilk indirmemi aldım ve neden olduğu konusunda kafam karıştı. Oh iyi.
Chris Young

33
(X% 2! = 0) {/ * x garip * /} ise, ama kim bilir. Java'yı da bilmiyorum.
eugensk

9
Karıştırmamızı oylayarak harcamak zorunda kalmadan, onu bitsel operatör moronlarından ayırt etmek için çok sayıda oy alıyor.
wnoise

13
Bir şey dışında her şeye katılıyorum: Tamsayıları ve doğruluk değerlerini kavramsal olarak ayrı tutmayı seviyorum, bu yüzden "if (x% 2 == 1)" yazmayı tercih ediyorum. Derleyici için de aynı, ama belki insanlar için biraz daha açık. Ayrıca, sıfır olmayanı doğru olarak yorumlamayan dillerde de aynı kodu kullanabilirsiniz.
Thomas Padron-McCarthy

46
Karşılaştırmam mı? Ne kriter? Ben kıyaslama yapmadım. Oluşturulan montaj dilini inceledim. Bunun printf ile kesinlikle bir ilgisi yok.
Chris Young

207

Sizler waaaaaaaay çok verimlisiniz. Gerçekten istediğiniz şey:

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

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

  return odd;
}

İçin tekrarlayın isEven.

Tabii ki, bu negatif sayılar için işe yaramıyor. Ama parlaklıkla fedakarlık gelir ...


17
Negatif değerler üzerinde bir argüman istisnası attıysanız ve belgelerde bu fonksiyonun O (N) olduğunu belirttiyseniz, bununla iyi olur.
Jeffrey L Whitledge

7
Kurumsal sürüm XML kullanmalıdır. Tabii ki günümüzde sorgulayabileceğiniz bir web hizmetiniz olacaktı
Martin Beckett

58
Bunu bir arama tablosu ile optimize etmelisiniz.
Weeble

1
Ben böyle bir keşişim, 6,999 temsilcinizi yeni bir milenyumda + 1'lemek zorunda kaldınız
Eran Medan

7
Bu harika! Patronum, Enterprise Lisansının Standart Lisanstan daha fazlasını vermediğini düşündüğü için kızgın bir müşterimiz olduğunu söyledi. Şimdi bu işlevi programımıza ekledik ve daha yavaş çalıştığı için, yazılımının WAY daha fazla iş yaptığını düşünüyor !!!
Phil

97

Bit aritmetiği kullanın:

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

Bu, bölme veya modül kullanmaktan daha hızlıdır.


43
Bölme veya modül kullanmaktan daha hızlı olduğunu söylemenin adil olduğunu düşünmüyorum. C standardı operatörlerin performansı hakkında hiçbir şey söylemez ve iyi bir derleyici de her ikisi için hızlı kod üretir. Şahsen niyetimi ileten deyimi seçerdim ve% burada daha uygun görünüyor
Chris Young

21
(X & 1) 'i daha iyi seviyorum, çünkü sayının insanların yaptığı gibi olup olmadığını kontrol ediyor: son basamağın çift mi yoksa tek mi olduğunu kontrol edin. Bence niyetini modulo yönteminden daha çok iletiyor. (Bu çok önemli değil.)
Jeremy Ruten

2
Haklısın, sanırım öznel. "Çift" in genel tanımı "0, 2, 4, 6 veya 8 ile biten tam sayı" değil, 2 ile bölünebilen tam sayıdır. :-)
Chris Young

4
@TraumaPony - ANSI standart C ve erken Java için bilgisayar sistemine bağlıdır. Modül daima gri kodlu vb 2'nin iltifat, 1'in iltifat, Ama modülü - O imzalı numaralar ne için kullanıldığını temsil belirtilmemiş var
Aaron

9
Negatif sayılar için evrensel olarak çalışmaz. Daha fazla ayrıntı için bu cevabı kontrol edin: Ayrıntılar için stackoverflow.com/questions/160930/… .
Andrew Edgecombe

36

[Şaka modu = "açık"]

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

[Şaka modu = "kapalı"]

DÜZENLEME: Numaralandırmaya kafa karıştırıcı değerler eklendi.


2
Vay be ... Bu SCdF'nin çözümünden daha çılgın! Kudos! Hayır upvote olsa ... bunu tavsiye edemez. Ama komik için teşekkürler!
Wes P

1
Bu yaklaşımın avantajı sayılardan daha fazlası ile çalışmasıdır. Ayrıca, bu satırı değiştirirseniz: char bar = foo [foo.Length - 1]; bununla: çift çubuk = Char.GetNumericValue (foo [foo.Length - 1]); Sonra herhangi bir sayı sistemi ile çalışacaktır.
Jeffrey L Whitledge

5
hata raporu: 14.65, bilinmesi gerektiğinde tuhaf olarak bildirilir.
TheSoftwareJedi

4
Yazılım Jedi, bu bir "özellik". ;)
Sklivvz

31
TheSoftwareJedi: 14.65 şimdiye kadar gördüğüm en garip tamsayılardan biri.
Bruce Alderman

16

Ffpf'ye yanıt olarak - yıllar önce bir meslektaşımla tam olarak aynı tartışmayı yaptım ve cevap hayır , negatif sayılarla çalışmıyor.

C standardı, negatif sayıların 3 şekilde temsil edilebileceğini öngörür:

  • 2'nin tamamlayıcısı
  • 1'in tamamlayıcısı
  • işaret ve büyüklük

Böyle kontrol ediliyor:

isEven = (x & 1);

2'nin tamamlayıcısı ve işaret ve büyüklük gösterimi için çalışır, ancak 1'in tamamlayıcısı için çalışmaz.

Ancak, aşağıdakilerin tüm durumlar için işe yarayacağına inanıyorum:

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

Metin kutusunun benim karakterimden sonra her şeyi yediğini gösteren ffpf sayesinde!


İkinci kod örneğinizde bazı metinler eksik.
Jeff Yates

3
Bu sayıları iltifat edelim!
thejh

14

Güzel bir tane:

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

Bu yöntemin iki işlevi içeren kuyruk özyineleme kullandığını unutmayın. Derleyiciniz bir Scheme derleyicisi gibi kuyruk özyinelemeyi destekliyorsa verimli bir şekilde (bir süre / döngü türüne dönüşebilir) uygulanabilir. Bu durumda, yığın taşmamalıdır!


1
Bu isOdd (0) 'ı iyi işlemez.
Steve McLeod

1
Ben herhangi bir çift değerlerle isOdd () veya herhangi bir tek değerlerle isEven () için sonsuz bir döngü (kuyruk özyineleme ile) veya bir yığın taşması (kuyruk özyineleme olmadan) var sanırım. Sadece doğru ile sona erer. Tekrar tekrar durma sorunu.
Jeffrey L Whitledge

7
Oh, tabi, yorum yapmadan düzeltin ve aptal gibi görünmemi sağlayın. Bu iyi.
Jeffrey L Whitledge

1
Şimdi, bir derleme hatası var: inEe bile tüm kod yolları bir değer döndürmez. Hayır, aslında bu kodu denemedim, kafamdaki şikayetçi derleyici.
Jeffrey L Whitledge

5
derleme hatası: tüm yollar örnek kodunuzdaki hata yorumlarıyla sizi bombalamaktan nefret etmez, ancak isEven (5)
Kevin

11

Bir sayı, ikiye bölündüğünde, geri kalan 0 ise bile. Bir sayı, 2'ye bölündüğünde, geri kalan 1 ise gariptir.

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

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

Yöntemler harika!


Negatif tek sayılar için num% 2 == -1 olduğu için Java yönteminiz bozuldu.
WMR

Beni bu yüzden mi düşürdün?
jjnguy

3
Bunu indirdim çünkü C'deki fonksiyonunuz yazmaktan daha fazla karakter alıyor. IE num% I, IsOdd (I) boşlukları dahil 7 karakterdir ve 8 karakterdir. Neden sadece işlemi yapmaktan daha uzun bir fonksiyon yaratıyorsunuz?
Kevin

13
@Kevin benim görüşümde kodlar tarafından değil, düşünme + hata ayıklama zamanı da dahil olmak üzere yazmanız gereken zamana göre ölçülür. num% 2, düşünülmesinin isOdd değerinden milisaniye daha uzun sürüyor. Şimdi sayıları global olarak ekleyin ve kolektif bir yılı kaybettiniz. Ayrıca isOdd test edilebilir ve doğrulanabilir ve nihayetinde% 2 olarak bazı ücretsiz geliştiricilerin her zaman bir şüphe ve denemeye sahip olacağı yerlerde hata ücretsiz sertifikalı (örn. negatif sayıları işleme). iyi kod yazmak değil kod, sadece yeniden ... sadece benim 2 sent.
Eran Medan

2
@EranMedan, Aynı mantık, i ++ 'ın IncrementByOne (i) ile değiştirilmesinde geçerli olacak ve aynı derecede kötü bir fikir. Bir geliştirici num% 2'nin ne yaptığından şüphe duyuyorsa, kodumun yakınında hiçbir yerde istemiyorum.
Kevin


7

Ben sadece 2 ile bölmek söyleyebilirim ve 0 kalan varsa, hatta, aksi takdirde garip.

(%) Modülünü kullanmak bunu kolaylaştırır.

Örneğin. % 4 2 = 0 bu nedenle 4 çift bile% 5 2 = 1 bu nedenle 5 tek


6

Soruna bir çözüm daha
(çocuklar oy kullanabilirler)

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

  }
  return half1 == half2;
}

Hayır,
güvendiğim

Bunu yok edecektim, ama negatif sayılarda biraz yavaş. :)
Chris Young

3
Tüm sayılar parlak ve pozitiftir. Yoksa bazılarına karşı önyargılı mısınız? :))
eugensk

3
Bilgisayarlarda, bir kez negatif olan tüm sayılar sonunda pozitif olur. Biz buna Mutluluk Rollover'ı diyoruz (BIGNUMS, YMMY için geçerli değildir, tüm eyaletlerde geçerli değildir).
Hartung

@ WillHartung "mutluluk devrilme" harika! : D
thejh

6

Ben tamsayıların pariteler bir tablo (0 olsa bile 1) inşa ediyorum (böylece bir arama yapabilir: D), ama gcc bana bu boyutlarda diziler yapmak izin vermez:

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

Öyleyse bunun yerine çift ve tekin matematiksel tanımına başvuralım.

N tamsayısı, n = 2k olacak şekilde bir k tamsayısı olsa bile olur.

N = 2k + 1 olacak şekilde k tamsayısı k varsa, n tamsayısı tuhaftır.

İşte bunun için kod:

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-tamsayılarının olası değerleri göstermesine izin verin int belirli bir C derlemesindeki . (C tam sayılarının tam sayıların bir alt kümesi olduğuna dikkat edin.)

Şimdi, C tamsayılarındaki belirli bir n için karşılık gelen tamsayı k'nın C tamsayılarında bulunmayabileceğinden endişe edilebilir. Ancak küçük bir kanıtla, tüm tamsayılar için n, | n | <= | 2n | (*), burada | n | "n pozitifse n ve aksi takdirde -n" dir. Başka bir deyişle, tamsayılardaki tüm n için aşağıdaki asgari tutardan en az biri (tam olarak ya vakalar (1 ve 2) ya da vakalar (3 ve 4) aslında ama bunu burada kanıtlamayacağım):

Durum 1: n <= 2n.

Durum 2: -n <= -2n.

Durum 3: -n <= 2n.

Durum 4: n <= -2n.

Şimdi 2k = n al. (N bile olsa böyle bir ak var, ama burada kanıtlayamayacağım. Eğer n bile değilse, döngü evenyine de erken dönemiyor, bu yüzden önemli değil.) Ama bu k <n 0 ile (*) değil ve (burada tekrar kanıtlanmadı) tüm m için, 2m = z tamsayılarındaki z, m'nin m'ye eşit olmadığı anlamına gelir, m 0 değildir. yani 0 bile yapılır (n = 0 ise 0 C-tamsayılarındadır, çünkü n fonksiyonda C-tamsayısındadır)even , dolayısıyla k = 0 C-tamsayılarındadır). Bu nedenle, C tamsayılarındaki bu ak, n eşitse, C tamsayılarındaki n için bulunur.

Benzer bir argüman, eğer n tuhafsa, C-tamsayılarında n = 2k + 1 olacak şekilde ak olduğunu gösterir.

Dolayısıyla fonksiyonları evenve oddburada sunulan tüm C-tamsayılar için düzgün çalışacaktır.


1
Suç demek istemiyorum, ama bu cevabın anlamı nedir? i % 2çok daha küçük ve muhtemelen daha verimlidir.
GManNickG

2
@GMan: Ama bu çok daha belirleyici! Bu, tüm kenar durumlarını doğru bir şekilde algılayacaktır.
P Daddy

1
... VE (!!!) doğru !!!
Thomas Eding

Şaka yapıp yapmadığınızı söyleyemem. : X %2tüm tamsayılar için çalışır.
GManNickG

1
+1: "İyi Cevap" diyecektim, ama bence "İlginç Cevap" daha uygun.
James Webster


4

İşte Java'da bir cevap:

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

4

Bunu dene: return (((a>>1)<<1) == a)

Misal:

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

b     =  10011100
-----------------
b>>1 --> 01001110
b<<1 --> 10011100

Bunu açıklayabilir misiniz lütfen? Bitsel operatörler hakkında çok bilgim yok
Abdul

Sağa ve sonra sola kaydırmak son bitinizi (en sağdaki) sıfırlar. Yeni sayı orijinal ile aynı ise, bu orijinal sayının son bitinin 0 olduğu anlamına gelir. Güncellenmiş cevabıma bir göz atın.
Kiril Aleksandrov

teşekkürler, şimdi anladım
Abdul

Hangi yaklaşımın daha hızlı olduğundan emin değilim. Ben onları karşılaştırmaya çalışmadım.
Kiril Aleksandrov

Bu da en önemli parçanızı sıfırlamıyor mu? Bazı dillerde imzasız ints ve çoğunda negatif ints ile ilgili bir sorun ...
Troyseph

4

Bu oldukça eğlenceli tartışmayı okurken, ana döngü içinde tek ve çift sayıları test eden gerçek dünyaya, zamana duyarlı bir işleve sahip olduğumu hatırladım. StackOverflow üzerinde başka bir yerde yayınlanan aşağıdaki gibi bir tamsayı güç işlevidir. Kriterler oldukça şaşırtıcıydı. En azından bu gerçek dünya işlevinde, modulo daha yavaş ve önemli ölçüde. Kazanan, modulo zamanının% 67'sini gerektiren geniş bir farkla, bir veya (|) yaklaşımıdır ve bu sayfanın başka bir yerinde bulunamaz.

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

300 milyon döngü için kıyaslama zamanlamaları aşağıdaki gibidir.

3.962 | ve maske yaklaşımı

4.851 & yaklaşımı

5.850% yaklaşımı

Teori ya da bir montaj dili listesi düşünen insanlar için bu gibi argümanlar ortaya koyar, bu bir uyarıcı masal olmalıdır. Cennette ve dünyada, Horatio'da, felsefenizde hayal edilenden daha fazla şey vardır.


1
Ne zaman uygulama unsigned xolarak x = x >> 1;tanımlandığında daha iyi tanımlanır davranış x < 0. Nedeni belirsiz xve OrMasktürü farklı. Bir while(x)test kullanarak yeniden yazmak için yeterince basit .
chux - Monica

2
Bu derleyiciyi karşılaştırmak için kullandığınız derleyiciyi merak ediyorum, çünkü çoğu derleyici % 2kasayı bitsel olarak kullanarak derleyecek kadar akıllı olmalıdır &. Bunu sadece test ettim ve sonuçlar tamamen aynı (VS2015, Release, tüm optimizasyonlarla, x86 ve x64 ile derlenir). Kabul edilen cevap bunu GCC için de belirtmektedir (2008'de yazılmıştır).
Lou

2
Bu yazı ile ilgili sorun , herhangi bir platform / derleyici üzerinde bir bitwise orbir daha hızlı olacağını öncül andoldukça düşük olmasıdır. Böyle garip bir platform / derleyici kombo olsa bile (ve kıyaslama yapmak için kullanılan kodu da göndermemiş olsanız bile), aynı davranacak diğer derleyicilere bağlı olarak kötü bir optimizasyon bahsi olacaktır. Yazdığım gibi, bunun hangi platform / derleyicinin test edildiğini merak ediyorum , çünkü doğru bir şekilde ölçülmediğinden neredeyse eminim.
Lou

2
Sana yalancı dememek, sadece doğru ölçmediğin yüksek bir kesinlikle iddia etmek. Henüz bir kamyon şoförü demeye gerek yok, orijinal yorumumu okudum: Bir kıyaslama yaptım ve sonuçlar beklendiği gibi, her üç durumda da tamamen aynıydı (her testi 500.000 için 10 kez yaptıktan sonra kesin olarak ~ 3 sigma) .000 yineleme). Gerçekten uzun bir şerefli kariyeriniz varsa, geri adım atın ve taleplerinizin mantıklı olup olmadığını düşünün, ardından karşılaştırma yapmak için kullanılan gerçek kodu gönderin. Aksi takdirde, gönderiye inandığım şey, sadece ölçümde bir hata.
Lou

1
Bitti .
Lou

4

Bu, @RocketRoy ile cevabı hakkındaki tartışmanın devamı niteliğindedir. , ancak bu sonuçları karşılaştırmak isteyen herkes için yararlı olabilir.

tl; dr Gördüğüm kadarıyla, Roy'un yaklaşımı ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) tamamen üzere optimize edilmemiştir x & 1olarakmod yaklaşımla, ama pratikte çalışan süreleri her durumda eşit çıkmak gerekir.

İlk olarak derlenmiş çıktıyı Compiler Explorer kullanarak karşılaştırdım :

Test edilen fonksiyonlar:

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

CLang 3.9.0 -O3 ile:

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 ile 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'a kadar şapkalar, üç vakanın da işlevsel olarak eşit olduğunu fark etti. Bununla birlikte, Roy'un yaklaşımı GCC'de optimize edilmediğinden YMMV.

Visual Studio ile benzer; bu üç işlev için sökme Bülteni x64 (VS2015) incelendiğinde, karşılaştırma bölümünün "mod" ve "ve" kasaları için eşit olduğunu ve Roy'un "veya" kutusu için biraz daha büyük olduğunu görebiliyordum:

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

Ancak, bu üç seçeneği karşılaştırmak için gerçek bir kıyaslama çalıştırdıktan sonra (düz mod, bitsel veya bitsel ve), sonuçlar tamamen eşitti (yine, Visual Studio 2005 x86 / x64, Release build, hata ayıklayıcı eklenmedi).

Sürüm montajı, testtalimatlar andve modvakalar için talimatlar kullanırken Roy'un davası cmp eax,0FFFFFFFFhyaklaşımı kullanır , ancak pratik olarak hiçbir fark olmaması için ağır bir şekilde açılmış ve optimize edilmiştir.

20 çalıştırmadan sonraki sonuçlarım (i7 3610QM, Windows 10 güç planı Yüksek Performans olarak ayarlandı):

[Test: Düz mod 2] ORTALAMA SÜRESİ: 689,29 ms (Nispi fark: +% 0,000)
[Test: Bitsel veya] ORTALAMA SÜRESİ: 689,63 ms (Nispi fark: +% 0,048)
[Test: Bitsel ve] ORTALAMA SÜRESİ: 687,80 ms (Bağıl fark: -0.217%)

Bu seçenekler arasındaki fark% 0.3'ten azdır, bu nedenle montajın her durumda eşit olduğu açıktır.

İşte kimse denemek istiyorsa kod, ben sadece Windows üzerinde test bir uyarı ile ( tanım için #if LINUXkoşullu kontrol edin get_timeve gerekirse bu cevap alınan ).

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

Kıyaslama Kardinal Günah işlemişsinizdir; o kadar özel bir şey yaratmak, gerçek dünyadaki bir ortamı temsil etmez. Montaj dilinize bakın ve ne kadar az kayıt kullandığınızı görün. Çaba için yüksek not, ancak bu sonuçlar gerçek dünyadaki işlemede geçerli olmayacak.

@RocketRoy: Tüm çıktılar üç durum için de aynı olduğundan (bir durumda programınız için biraz daha kötü), kaç kayıt kullanıldığını gerçekten umursamıyorum. Fakat yine de, derleyiciyi vakalardan birinde daha optimize bir montaj oluşturmak için karıştıracak, diğer her şey eşit olan böyle bir program / ortam oluşturmaktan ve yayınlamaktan çekinmeyin.
Lou

Her zaman kendini beğenmiş programcıları severim. Bir programcının sahip olması iyi bir özelliktir, ancak daha karmaşık, gerçek dünya programında yöntemim sizinkinden daha iyi performans gösterecektir, çünkü derleyicinin sorunu çözmek için daha fazla yolu vardır, böylece talimatlar çakışır (Intel mimarileri üzerinde) daha iyi sonuçlar verir . Kıyaslama deneyimine sahip çok az deneyimli programcı karşılaştırmanızı tercih eder, ancak iyi çalışmaya devam eder ve yeni çip sürümleri çıktığında kıyaslamalarınızı tekrar çalıştırmayı unutmayın. Zaman içinde işler değişiyor.

3

Bunun sadece sözdizimsel şeker olduğunu ve sadece .net'te uygulanabileceğini biliyorum ama ya uzatma yöntemi ...

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

Şimdi aşağıdakileri yapabilirsiniz

int i = 5;
if (i.IsOdd())
{
    // Do something...
}

1
Güzel kod. Yazık ki 2 tuhaf, 3 değil.
Anthony

ayy, üzgünüm ... benim mantık yanlış bir şekilde yuvarlak ...
rudigrobler

3

"Yaratıcı ama kafa karıştırıcı kategorisinde" teklif ediyorum:

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

Bu temadaki Microsoft C ++ 'a özgü bir varyant:

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

2

Bitsel yöntem, tamsayının iç gösterimine bağlıdır. Modulo, bir modulo operatörünün bulunduğu her yerde çalışacaktır. Örneğin, bazı sistemler etiketleme için (dinamik diller gibi) aslında düşük seviye bitleri kullanır, bu nedenle raw x & 1 aslında bu durumda çalışmaz.


2

IsOdd (int x) {dönüş doğru; }

Doğruluk kanıtı - tüm pozitif tamsayıların kümesini göz önünde bulundurun ve garip olmayan boş olmayan bir tamsayılar kümesi olduğunu varsayalım. Pozitif tamsayılar iyi sıralandığından, kendi başına oldukça garip olmayan en küçük tek bir sayı olacaktır, bu yüzden açıkça bu sayı kümede olamaz. Bu nedenle bu küme boş bırakılamaz. En büyük tek sayı için arama dışında negatif tamsayıları tekrarlayın


2

Taşınabilir:

i % 2 ? odd : even;

unportable:

i & 1 ? odd : even;

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

2

Bazı insanların gönderdiği gibi, bunu yapmanın birçok yolu vardır. Bu web sitesine göre , en hızlı yol modül operatörüdür:

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

Bununla birlikte, yukarıdaki ortak modül işleminden daha yavaş çalışan, yazar tarafından işaretlenmiş başka bir kod :

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

Kaç kişi Math.System.DivRem yöntemini biliyordu ya da neden kullanıyorlar ??



1

Çalışmalarımız sırasında çok boole cebiri yapmayanlarımız için bitsel operatör yöntemi hakkında daha fazla ayrıntı vermek için burada bir açıklama var. Muhtemelen OP için çok fazla faydası yok, ancak NUMBER & 1'in neden çalıştığını açıklamak gibi hissettim.

Lütfen yukarıda yanıtladığı gibi, negatif sayıların temsil edilme şeklinin bu yöntemin çalışmasını durdurabileceğini unutmayın. Aslında modulo operatör yöntemini bile kırabilir, çünkü her dil negatif işlenenlerle nasıl başa çıktığı konusunda farklılık gösterebilir.

Ancak NUMBER'in her zaman olumlu olacağını biliyorsanız, bu iyi çalışır.

Yukarıdaki Tooony'nin, ikili (ve denary) sadece son basamağın önemli olduğuna işaret ettiği gibi.

Bir boole mantığı VE geçidi, her iki girişin de 1'in döndürülmesi için 1 (veya yüksek voltaj) olması gerektiğini belirtir.

1 & 0 = 0.

0 & 1 = 0.

0 & 0 = 0.

1 & 1 = 1.

İkili olarak herhangi bir sayıyı temsil ediyorsanız (burada 8 bitlik bir gösterim kullandım), tek sayıların sonunda 1, hatta sayıların 0 vardır.

Örneğin:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Herhangi bir sayı alır ve bitsel AND (& in java) kullanırsanız 1 ile 00000001, = 1 döndürür, yani sayı gariptir. Veya 00000000 = 0, yani sayı çifttir.

Örneğin

Garip?

1 & 1 =

00000001 ve

00000001 =

00000001 <- Oran

2 & 1 =

00000010 ve

00000001 =

00000000 <- Çift

54 ve 1 =

00000001 ve

00110110 =

00000000 <- Çift

Bu yüzden bu işe yarar:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Gerekirse özür dilerim.


1

Sıfır Eşlik Sayısı | sıfır http://tinyurl.com/oexhr3k

Python kod dizisi.

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

Çıktı:

   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

@ el.pescado, Teşekkürler. Sıfır ise, kaç çift var?

@ el.pescado, Tamam, Sana katılıyorum. O zaman, Eğer biraz düşünürsek, neden 2'ye (ikiye) bölünüyoruz? Bilmek istediğimiz şey, ikiye bölündüğümüzde? Neden 3 veya 5'e, vb.

@ el.pescado Bu Wikipedia makalesi Zero Paritesi yanlış. Bu makalede birçok insan kandırıldı. Wink'ten önce düşün.

1
Haklısın. Şimdi diğer cevapları okuduğum için seninkini en kapsamlı buldum :)
el.pescado

@ el.pescado. Teşekkür ederim. :) Artık Sıfır'ın en iyi arkadaşısın. (hug)

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

0

Tartışma uğruna ...

Çift veya tek olup olmadığını görmek için herhangi bir sayıdaki son basamağa bakmanız yeterlidir. İmzalı, imzasız, pozitif, negatif - hepsi bu konuda aynı. Yani bu her yerde çalışmalı: -

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

Buradaki anahtar üçüncü kod satırındadır, bölüm operatörü bir tamsayı bölümü gerçekleştirir, böylece sonuç sonucun kesir kısmını kaçırır. Yani örneğin 222/10 sonuç olarak 22 verecektir. Sonra tekrar 10 ile çarpın ve 220'niz var. Orijinal 222'den çıkarın ve büyü ile orijinal sayının son basamağıyla aynı sayı olan 2 ile bitirin. ;-) Parantez bize hesaplamanın yapıldığı sırayı hatırlatmak için var. Önce bölümü ve çarpımı yapın, ardından sonucu orijinal sayıdan çıkarın. Bunları dışarıda bırakabiliriz, çünkü bölünme ve çarpma için öncelik çıkarma işleminden daha yüksektir, ancak bu bize "daha okunabilir" kod verir.

Eğer isteseydik hepsini okunamaz hale getirebilirdik. Modern bir derleyici için hiçbir fark yaratmaz: -

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

Ancak, kodun gelecekte sürdürülmesini zorlaştıracaktır. Tek sayılar için metni "çift değil" olarak değiştirmek istediğinizi düşünün. Sonra başka biri daha sonra hangi değişiklikleri yaptığınızı öğrenmek ve bir svn diff veya benzeri yapmak istiyorum ...

Taşınabilirlik hakkında değil, hız hakkında daha fazla endişeleniyorsanız, en az önemli bite bakabilirsiniz. Bu bit 1 olarak ayarlanırsa, tek bir sayıdır, 0 ise çift bir sayıdır. Küçük bir endian sisteminde, Intel'in x86 mimarisi gibi şöyle bir şey olurdu:

if (iToTest & 1) {
  // Even
} else {
  // Odd
}

Sadece iToTest% 2 == 0'a gitmenin tam olarak nesi yanlış? Son basamağı çıkartan bir bölümü israf ediyorsunuz, bu yüzden sizinki olması gerektiği kadar iki katı.
freespace

@freespace: Bundan daha fazla israf ediyorum, değil mi? :-) Bir çarpma ve çıkarma da. Ama söylemeye cesaret edemediğim iki çözüm arasında en etkili olanı. Bu yazının ilk satırını tekrar okursanız, bunun en hızlı çözüm olduğunu iddia etmedim.
Tooony

@Tooony, ah, mizah şapkam düştü. Şu anda resmen geri döndü: D Üzgünüm :)
freespace

0

Verimli olmak istiyorsanız, bitsel operatörler ( x & 1) kullanın , ancak okunabilir olmak istiyorsanız modulo 2 ( x % 2) kullanın


-1: Verimli olmak istiyorsanız, ikisinden birini kullanın. Taşınabilir olmasını istiyorsanız kullanın %. Okunabilir olmasını istiyorsanız kullanın %. Hmmm, burada bir model görüyorum.
Thomas Eding

@trinithis, desen yoktur ve bu çözüm sizinkinden çok daha iyidir.
Subs

0

Çift veya tek kontrol etmek basit bir iştir.

Tam olarak 2'ye bölünebilen herhangi bir sayının çift sayı olan tek sayı olduğunu biliyoruz.

Sadece herhangi bir sayının bölünebilirliğini kontrol etmemiz ve %operatörü kullandığımız bölünebilirliği kontrol etmemiz gerekir

Aksi takdirde tek tutam kullanarak kontrol etme

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

C programı çift veya tek kullanarak kullanarak kontrol etmek

Koşullu / Üçlü işleci kullanma

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

C programı koşullu operatör kullanarak çift veya tek kontrol etmek .

Bitwise operatörünü kullanma

if(num & 1)  
{
    printf("Odd");
}
else 
{
    printf("Even");
}

ve üçlü operatör tam olarak nerede?
Beyondo

0

+% 66 daha hızlı>!(i%2) / i%2 == 0

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

Kod, eğer tamsayının son bitini kontrol eder 1 Binary

açıklama

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...

En doğru bitin Tek sayılar için her zaman 1 olduğuna dikkat edin .

& bizim en sağdaki bit lojik AND operatörü kontrolleri dönüş hattı durum 1 ise

Bunu doğru ve yanlış olarak düşün

Biz karşılaştırdığımızda n ile 1 hangi yollarla 0001ikilik sistemde (sıfırların sayısı önemli değil).
o zaman sadece 1 bayt büyüklüğünde n tamsayısına sahip olduğumuzu düşünelim .

8-bit / 8-ikili basamaklarla temsil edilir.

İnt Eğer n oldu 7 ve biz ile karşılaştırın 1 , Sanki

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

Hangi F yanlış, T de doğrudur.

İkisi de doğruysa en sağdaki biti karşılaştırır . Yani, otomajik 7 & 1olarak T rue.

Biti en sağdan önce kontrol etmek istersem ne olur?

İkili n & 1olarak n & 2hangi 2'yi temsil ettiğinizi değiştirin 0010.

Bitsel işlemlere yeni başlıyorsanız onaltılık gösterimi kullanmanızı öneririm
return n & 1;>> return n & 0x01;.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.