Jedno z bardzo trudnych pytań zadawanych w wywiadzie.
Zamień wartości dwóch zmiennych, takich jak a=10
i b=15
.
Generalnie, aby zamienić wartości dwóch zmiennych, potrzebujemy trzeciej zmiennej, takiej jak:
temp=a;
a=b;
b=temp;
Teraz wymaganiem jest, zamień wartości dwóch zmiennych bez używania trzeciej zmiennej.
Odpowiedzi:
Korzystanie z algorytmu wymiany xor
Dlaczego test?
Test ma na celu upewnienie się, że x i y mają różne lokalizacje pamięci (zamiast różnych wartości). Dzieje się tak, ponieważ
(p xor p) = 0
i jeśli oba x i y mają tę samą lokalizację pamięci, gdy jeden jest ustawiony na 0, oba są ustawione na 0. Gdy oba * x i * y mają wartość 0, wszystkie inne operacje xor na * x i * y będą równe 0 (ponieważ są takie same), co oznacza, że funkcja ustawi zarówno * x, jak i * y na 0.Jeśli mają te same wartości, ale nie to samo miejsce w pamięci, wszystko działa zgodnie z oczekiwaniami
Czy powinno to być używane?
W ogólnych przypadkach nie. Kompilator zoptymalizuje zmienną tymczasową i biorąc pod uwagę, że zamiana jest powszechną procedurą, powinien wypisać optymalny kod maszynowy dla twojej platformy.
Weźmy na przykład ten szybki program testowy napisany w C.
Skompilowano przy użyciu:
Wersja xor trwa
Gdzie jako wersja ze zmienną tymczasową przyjmuje:
źródło
ogólna forma to:
jednak trzeba potencjalnie uważać na przepełnienia, a także nie wszystkie operacje mają odwrotność, która jest dobrze zdefiniowana dla wszystkich wartości, które są zdefiniowane. np. * i / działa, aż A lub B będzie równe 0
xor jest szczególnie przyjemny, ponieważ jest zdefiniowany dla wszystkich int i jest jego własną odwrotnością
źródło
źródło
a+b
przepełnia?int
,unsigned short
...), to nadal działa w końcu, nawet z przelewem, bo jeśli a + b przelewa, a następnie - b będzie niedopełniony. W przypadku tych podstawowych typów całkowitych wartości są po prostu przenoszone.unsigned
typach całkowitych jest OK i działa zawsze. W przypadku typów podpisanych wywołałoby to niezdefiniowane zachowanie w przypadku przepełnienia.Nikt jeszcze nie zasugerował używania
std::swap
.Nie używam żadnych zmiennych tymczasowych iw zależności od typu
a
ib
implementacji może mieć specjalizację, która też nie. Implementację należy napisać, wiedząc, czy „sztuczka” jest odpowiednia, czy nie. Nie ma sensu próbować odgadnąć.Mówiąc bardziej ogólnie, prawdopodobnie chciałbym zrobić coś takiego, ponieważ zadziałałoby to dla typów klas umożliwiających ADL znalezienie lepszego przeciążenia, jeśli to możliwe.
Oczywiście reakcja ankietera na tę odpowiedź może wiele powiedzieć o wakacie.
źródło
std::
zdefiniowanychswap
przez użytkownika dla typów zdefiniowanych przez użytkownika są również dostępne do wywołania (to oznacza „zadziałałoby dla typów klas umożliwiających ADL znalezienie lepszego przeciążenia, jeśli to możliwe”).std::swap
używa dodatkowej pamięci tymczasowej w swojej implementacji nie? cplusplus.com/reference/utility/swapJak już zauważył manu, algorytm XOR jest popularnym algorytmem, który działa dla wszystkich wartości całkowitych (które zawierają wtedy wskaźniki, przy odrobinie szczęścia i rzutowaniu). Ze względu na kompletność chciałbym wspomnieć o innym mniej wydajnym algorytmie z dodawaniem / odejmowaniem:
Tutaj musisz uważać na przepełnienia / niedomiary, ale poza tym działa równie dobrze. Możesz nawet spróbować tego na pływakach / podwójnych w przypadku, gdy XOR nie jest na nich dozwolony.
źródło
std::swap()
? Jasne, możesz konwertować wskaźniki na liczby całkowite iz powrotem (zakładając, że istnieje wystarczająco duży typ liczby całkowitej, co nie jest gwarantowane), ale nie to sugerowałeś; zasugerowałeś, że wskaźniki są liczbami całkowitymi, a nie, że można je przekształcić w liczby całkowite. Tak, zaakceptowana odpowiedź nie będzie działać w przypadku wskaźników. Odpowiedź Charles Bailey użyciustd::swap
jest naprawdę jedyny słuszny.std::swap
można zaimplementować bez użycia trzeciej zmiennej.Głupie pytania zasługują na odpowiednie odpowiedzi:
Jedyne dobre użycie
register
słowa kluczowego.źródło
Zamiana dwóch liczb za pomocą trzeciej zmiennej wygląda tak:
Zamiana dwóch liczb bez użycia trzeciej zmiennej
źródło
źródło
cout << "Enter the two no=:"
jak spodziewałem się przeczytaćcout << "Now enter the two no in reverse order:"
a+b
luba-b
przepełnienie. Ponadtovoid main()
jest nieprawidłowy i<conio.h>
niestandardowy.Ponieważ oryginalne rozwiązanie to:
Możesz zrobić z niego dwie wkładki, używając:
Następnie zrób z tego jedną wkładkę, używając:
źródło
y
jest odczytywana i zapisywana w tym samym wyrażeniu bez interwencji punktu sekwencji.Jeśli zmienisz trochę pytanie, aby zadać pytanie o 2 rejestry asemblerowe zamiast zmiennych, możesz użyć
xchg
operacji jako jednej opcji, a operacji na stosie jako innej.źródło
xchg
jakiej operacji masz na myśli? Pytanie nie określało architektury procesora.Rozważmy
a=10
,b=15
:źródło
a=10
ib=1.5
.Aktualizacja: W tym przypadku pobieramy dane wejściowe z dwóch liczb całkowitych od użytkownika. Następnie używamy bitowej operacji XOR , aby je zamienić.
Że mamy dwie liczby całkowite
a=4
ib=9
, a następnie:źródło
Oto jeszcze jedno rozwiązanie, ale jedno ryzyko.
kod:
każda wartość w lokalizacji a + 1 zostanie nadpisana.
źródło
*(&a+1)
może byćb
.void main()
jest nieważny.<conio.h>
jest niestandardowy (i nawet go nie używasz).Oczywiście odpowiedź C ++ powinna brzmieć
std::swap
.Jednak nie ma również trzeciej zmiennej w następującej implementacji
swap
:Lub jako jednowierszowe:
źródło
źródło
to jest poprawny algorytm wymiany XOR
musisz upewnić się, że lokalizacje pamięci są różne, a także, że rzeczywiste wartości są różne, ponieważ A XOR A = 0
źródło
Możesz to zrobić… w prosty sposób… w ramach jednej linii Logiki
lub
źródło
b
jest zarówno odczytywany, jak i modyfikowany w ramach tego samego wyrażenia, bez pośredniego punktu sekwencji.źródło
Najlepszą odpowiedzią byłoby użycie XOR i użycie go w jednej linii byłoby fajne.
x, y to zmienne, a przecinek między nimi wprowadza punkty sekwencji, więc nie zależy od kompilatora. Twoje zdrowie!
źródło
Zobaczmy prosty przykład w c, aby zamienić dwie liczby bez użycia trzeciej zmiennej.
program 1:
Wynik:
Przed zamianą a = 10 b = 20 Po zamianie a = 20 b = 10
Program 2: Używanie * i /
Zobaczmy inny przykład zamiany dwóch liczb za pomocą * i /.
Wynik:
Przed zamianą a = 10 b = 20 Po zamianie a = 20 b = 10
Program 3: Wykorzystanie bitowego operatora XOR:
Bitowy operator XOR może służyć do zamiany dwóch zmiennych. XOR dwóch liczb xiy zwraca liczbę, która ma wszystkie bity jako 1, gdziekolwiek bity x i y różnią się. Na przykład XOR z 10 (binarnie 1010) i 5 (binarnie 0101) to 1111, a XOR z 7 (0111) i 5 (0101) to (0010).
Wynik:
Po zamianie: x = 5, y = 10
Program 4:
Nikt jeszcze nie zasugerował używania std :: swap.
Nie używam żadnych zmiennych tymczasowych iw zależności od typu aib implementacja może mieć specjalizację, która też nie. Implementację należy napisać, wiedząc, czy „sztuczka” jest odpowiednia, czy nie.
Problemy z powyższymi metodami:
1) Podejście oparte na mnożeniu i dzieleniu nie działa, jeśli jedna z liczb jest równa 0, ponieważ iloczyn staje się 0 niezależnie od drugiej liczby.
2) Oba rozwiązania arytmetyczne mogą powodować przepełnienie arytmetyczne. Jeśli x i y są zbyt duże, dodawanie i mnożenie może wyjść poza zakres liczb całkowitych.
3) Kiedy używamy wskaźników do zmiennej i dokonujemy zamiany funkcji, wszystkie powyższe metody zawodzą, gdy oba wskaźniki wskazują na tę samą zmienną. Zobaczmy, co się stanie w tym przypadku, jeśli oba wskazują na tę samą zmienną.
// Metoda oparta na bitowym XOR
// Metoda oparta na arytmetyce
Zobaczmy następujący program.
Wyjście :
Po zamianie (& x, & x): x = 0
Zamiana zmiennej może być potrzebna w wielu standardowych algorytmach. Na przykład zobacz tę implementację QuickSort, w której możemy zamienić zmienną ze sobą. Powyższego problemu można uniknąć, stawiając warunek przed zamianą.
Wyjście :
Po zamianie (& x, & x): x = 10
źródło
Może nie na temat, ale jeśli wiesz, co zamieniasz pojedynczą zmienną między dwiema różnymi wartościami, możesz być w stanie wykonać logikę tablicową. Za każdym razem, gdy ten wiersz kodu zostanie uruchomiony, zamieni on wartość między 1 a 2.
źródło
Mógłbyś:
Lub użyj make_tuple podczas zamiany więcej niż dwóch zmiennych:
Ale nie jestem pewien, czy wewnętrznie std :: tie używa zmiennej tymczasowej, czy nie!
źródło
W javascript:
Zwróć uwagę na czas wykonania obu opcji.
Po uruchomieniu tego kodu zmierzyłem go.
Jak widać (i jak wielu powiedziało), zamiana na miejscu (xor) zajmuje dużo czasu niż inna opcja wykorzystująca zmienną temp.
źródło
R brakuje równoczesnego przypisania, jak zaproponował Edsger W. Dijkstra w A Discipline of Programming , 1976, rozdz. 4, s. 29. Pozwoliłoby to na eleganckie rozwiązanie:
źródło
To bardzo proste, ale może budzić ostrzeżenie.
źródło
b=a
jest wykonywana przed, czy poa + b
.jednoliniowe rozwiązanie do zamiany dwóch wartości w języku c.
źródło
źródło