2011-12-05 9 views
9

Witam wszystkich zastanawiałem się, czy istnieje sposób wdrożenia tej metody bez przesyłania do szerszego typu danych (np. długi, podwójny itp.)?Czy istnieje algorytm określający, czy a * b mieści się w możliwych wartościach liczby całkowitej? (bez rzutowania na szerszy typ)

CanTimes(int a, int b){ 
    returns true if a * b is within the range of -2^31 to 2^31-1, else false; 
} 

Na przykład moglibyśmy wdrożyć jedną dla metody CanAdd (bez poświaty) jako takie:

public static boolean CanPlus(int a, int b) { 
     if (b >= 0) { 
      return a <= Integer.MAX_VALUE - b 
     } else { 
      return a >= Integer.MIN_VALUE - b 
     } 
    } 

język Wdrożenie jest Java, choć oczywiście jest to większym problemem językowym-agnostykiem.

Zastanawiam się, czy istnieje jakaś logika, którą możemy zastosować, aby zdecydować, czy a * b mieści się w zakresie liczby całkowitej bez rzucania go do szerszego typu danych?

Rozwiązanie! na podstawie komentarza zStrelok:

public static boolean CanTimes(int a, int b) { 
    if (a == 0 || b == 0) { 
     return true; 
    } 
    if (a > 0) { 
     if (b > 0) { 
      return a <= Integer.MAX_VALUE/b; 
     } else { 
      return a <= Integer.MIN_VALUE/b; 
     } 
    } else { 
     if (b > 0) { 
      return b <= Integer.MIN_VALUE/a; 
     } else { 
      return a <= -Integer.MAX_VALUE/b; 
     } 
    } 
} 
+0

Dlaczego? Dlaczego sztuczne ograniczenie? – EJP

+0

Może możesz zrobić coś z 'Integer.numberOfLeadingZeros'. Jeśli suma obu wiodących zer jest większa niż 31, nadal będzie to int lub coś podobnego. – Thilo

+0

@EJP To nie jest sztuczne ograniczenie, zastanawiam się, czy istnieje sposób, aby to zrobić bez konieczności przesyłania do szerszego typu danych. Na przykład HeadGeek ma pół-rozwiązanie tutaj http://stackoverflow.com/a/199455/632951. Jego szacunek jest jednak interesujący, w jaki sposób możemy go ulepszyć, tak aby faktycznie działał. – Pacerier

Odpowiedz

1

Jak na mój komentarz, tutaj jest wersja przystosowana, z niektórych testów jednostkowych:

public static int mulAndCheck(int a, int b) 
{ 
    int ret; 
    String msg = "overflow: multiply"; 
    if (a > b) 
    { 
     // use symmetry to reduce boundry cases 
     ret = mulAndCheck(b, a); 
    } 
    else 
    { 
     if (a < 0) 
     { 
      if (b < 0) 
      { 
       // check for positive overflow with negative a, negative b 
       if (a >= Integer.MAX_VALUE/b) 
       { 
        ret = a * b; 
       } 
       else 
       { 
        throw new ArithmeticException(msg); 
       } 
      } 
      else if (b > 0) 
      { 
       // check for negative overflow with negative a, positive b 
       if (Integer.MIN_VALUE/b <= a) 
       { 
        ret = a * b; 
       } 
       else 
       { 
        throw new ArithmeticException(msg); 

       } 
      } 
      else 
      { 
       // assert b == 0 
       ret = 0; 
      } 
     } 
     else if (a > 0) 
     { 
      // assert a > 0 
      // assert b > 0 

      // check for positive overflow with positive a, positive b 
      if (a <= Integer.MAX_VALUE/b) 
      { 
       ret = a * b; 
      } 
      else 
      { 
       throw new ArithmeticException(msg); 
      } 
     } 
     else 
     { 
      // assert a == 0 
      ret = 0; 
     } 
    } 
    return ret; 
} 

@Test(expected = ArithmeticException.class) 
public void testOverflow() 
{ 
    mulAndCheck(Integer.MAX_VALUE, Integer.MAX_VALUE); 
} 

@Test(expected = ArithmeticException.class) 
public void testOverflow1() 
{ 
    mulAndCheck(Integer.MIN_VALUE, Integer.MAX_VALUE); 
} 

@Test 
public void testTimesMinus1() 
{ 
    Assert.assertEquals(Integer.MIN_VALUE + 1, mulAndCheck(Integer.MAX_VALUE, -1)); 
    Assert.assertEquals(Integer.MAX_VALUE, mulAndCheck(Integer.MIN_VALUE + 1, -1)); 
} 
+0

to jawnym [plagiatorstwo] (http://dictionary.reference.com/browse/plagiarism) z [tego post] (http://www.java2s.com/Tutorial/Java/0040__Data-Type/Multiplytwolongintegerscheckingforoverflow.htm) ! – Bohemian

+0

@Bohemian Jestem prawie pewien, że zamieściłem tam, gdzie jest to zaadaptowane z mojego komentarza do OP: '" Pojawiła się niewielka liczba Googling: java2s.com/Tutorial/Java/0040__Data-Type/... co jest dla długich , ale oczywiście jest to tylko kilka sekund od zaadoptowania do ints. "W każdym razie, nie dodajecie nic wartościowego do dyskusji tutaj. – Strelok

1

Można wykonać mnożenie, a następnie sprawdzić, czy podzielenie przez jednego czynnika nadal daje drugą.

EDIT

Powyższe nie działa cały czas, jak Dietrich Epp zaznacza; nie powiedzie się dla -1 i Integer.MIN_VALUE. Nie wiem, czy są jakieś inne przypadki krańcowe. Jeśli nie, to łatwo byłoby sprawdzić tę jedną sprawę.

+0

Interesujące. Czy to działa niezawodnie? A może są przypadki skrajne, które powodują fałszywe alarmy? – Thilo

+0

Algorytm zakończy się niepowodzeniem dla 'a = -1',' b = -0x80000000'. 'a * b = -0x80000000' (przepełnienie), ale' (a * b)/a == b', ponieważ przepełnienie występuje również podczas sprawdzania. –

+1

@Thilo - Myślałem, że jest niezawodny, ale mały przykład (jeśli zrobiłem to dobrze) sprawia, że ​​myślę, że nie jest. W 2-bitowej arytmetyce dopełnienia 2-go, 11 * 10 (tj. -1 * -2) daje 10 (co jest błędne). Ale 10/11 daje 10, więc przeszedłby test. Lepszym testem byłoby podzielenie największej (magnitudo) liczby dozwolonej przez jeden czynnik i sprawdzenie, czy wynik jest mniejszy (w skali) niż inny czynnik. Oddzielne testy są potrzebne dla każdej kombinacji znaków, tak myślę. –

-1

Matematycznie suma dziennika bazy-2 powinna być mniejsza niż 2 . Niestety, Math nie daje nam logarytm przy podstawie 2, ale to jest nadal dość proste:

static boolean canMultiply(int a, int b) { 
    return Math.log(Math.abs(a)) + Math.log(Math.abs(b)) <= Math.log(Integer.MAX_VALUE); 
} 

edycja: Ze względu na (fair) flak, jak o tym prostym podejściu, która dotyczy dokładnie pytanie OP?

static boolean canMultiply(int a, int b) { 
    return a == 0 || ((a * b)/a) == b; 
} 

Jeśli wystąpi przepełnienie, podzielenie przez pierwotny numer nie spowoduje powrotu do numeru początkowego.
Co ważne, zadziała to w przypadku longs, której nie można odrzucić .

+1

heys cool = D, jednak Math.log zmienia go w double = X – Pacerier

+0

To nie działa cały czas. Jeśli (matematyczny) produkt jest ujemny, limitem do sprawdzenia jest "-Integer.MIN_VALUE". –

+0

@TedHopp Tak - istnieje przypadek krawędzi produktu, który jest dokładnie taki * * Integer.MIN_VALUE' nie jest pokryty, ale 'log' nie jest tak precyzyjny. Jeśli możesz żyć z tym skrajnym przypadkiem, to rozwiązanie byłoby do przyjęcia. (Używanie 'Math.abs()' sprawia, że ​​negatywne wyniki są nadal poprawnie sprawdzane) – Bohemian

1

Ponieważ mnożenie a*b jest taka sama jak a+a+a+... powtarzane b razy (i vice versa), można zrobić coś takiego:

(I zmieniono nazwę funkcji CanMultiple() do isIntMultiplication(), ponieważ myślę, że to bardziej jasne)

public boolean isIntMultiplication(int a, int b) { 
    // signs are not important in this context 
    a = Math.abs(a); 
    b = Math.abs(b); 
    // optimization: I want to calculate a*b as the sum of a by itself repeated b times, so make sure b is the smaller one 
    // i.e., 100*2 is calculated as 100+100 which is faster than summing 2+2+2+... a hundred times 
    if (b > a) { int swap = a; a = b; b = swap; } 

    int n = 0, total = a; 
    while(++n < b) { 
     if (total <= Integer.MAX_VALUE - a) { 
      total += a; 
     } else { 
      return false; 
     } 
    } 
    return true; 
} 

zobaczyć go w akcji:

// returns true, Integer.MAX_VALUE * 1 is still an int  
isIntMultiplication(Integer.MAX_VALUE, 1); 

// returns false, Integer.MAX_VALUE * 2 is a long  
isIntMultiplication(Integer.MAX_VALUE, 2); 

// returns true, Integer.MAX_VALUE/2 * 2 is still an int 
isIntMultiplication(Integer.MAX_VALUE/2, 2); 

// returns false, Integer.MAX_VALUE * Integer.MAX_VALUE is a long 
isIntMultiplication(Integer.MAX_VALUE, Integer.MAX_VALUE); 

To rozwiązanie nie wymaga użycia typów long.

+0

Znaki są ważne, ponieważ -Integer.MIN_VALUE> Integer.MAX_VALUE (matematycznie). –

+0

Hmm, podejście działa (chociaż znak nie wymaga sortowania), ale myślę, że dodatki 64k w najgorszym przypadku mogą być prawdopodobnie pobite za wydajność ... –

+0

Podoba mi się pomysł zamiany lewej i prawej strony wybierając mniejszy na zapętla się, choć wciąż jest dość powolna. – Pacerier

Powiązane problemy