Hàm thiết kế f (f (n)) == -n


841

Một câu hỏi tôi nhận được trong cuộc phỏng vấn cuối cùng của mình:

Thiết kế một chức năng f, sao cho:

f(f(n)) == -n

Số nguyên kýn 32 bit ở đâu ; bạn không thể sử dụng số học số phức.

Nếu bạn không thể thiết kế một hàm như vậy cho toàn bộ phạm vi số, hãy thiết kế nó cho phạm vi lớn nhất có thể.

Có ý kiến ​​gì không?


2
Cuộc phỏng vấn này được thực hiện cho công việc gì?
tymtam

Câu trả lời:


377

Làm thế nào về:

f (n) = dấu (n) - (-1) n * n

Trong Python:

def f(n): 
    if n == 0: return 0
    if n >= 0:
        if n % 2 == 1: 
            return n + 1
        else: 
            return -1 * (n - 1)
    else:
        if n % 2 == 1:
            return n - 1
        else:
            return -1 * (n + 1)

Python tự động thúc đẩy các số nguyên cho độ dài tùy ý. Trong các ngôn ngữ khác, số nguyên dương lớn nhất sẽ tràn, vì vậy nó sẽ hoạt động cho tất cả các số nguyên ngoại trừ số nguyên đó.


Để làm cho nó hoạt động với số thực, bạn cần thay n trong (-1) n bằng { ceiling(n) if n>0; floor(n) if n<0 }.

Trong C # (hoạt động cho mọi gấp đôi, ngoại trừ trong các tình huống tràn):

static double F(double n)
{
    if (n == 0) return 0;

    if (n < 0)
        return ((long)Math.Ceiling(n) % 2 == 0) ? (n + 1) : (-1 * (n - 1));
    else
        return ((long)Math.Floor(n) % 2 == 0) ? (n - 1) : (-1 * (n + 1));
}

10
Bị hỏng cho -1, vì -1 * 0 vẫn là 0
Joel Coehoorn

3
Không, không. f (-1) = 0. f (0) = 1
1800 THÔNG TIN

5
Nó bị hỏng cho 1 tuy nhiên. f (1) = 0. f (0) = 1
1800 THÔNG TIN

18
Hmm, lưu trạng thái với số chẵn và số lẻ, tôi nên nghĩ về điều đó.
Không biết

38
Tôi nghĩ rằng điều quan trọng nhất không phải là chức năng thực tế (có vô số giải pháp), mà là quá trình bạn có thể xây dựng một chức năng như vậy.
pyon

440

Bạn đã không nói loại ngôn ngữ nào họ mong đợi ... Đây là một giải pháp tĩnh (Haskell). Về cơ bản, nó gây rối với 2 bit quan trọng nhất:

f :: Int -> Int
f x | (testBit x 30 /= testBit x 31) = negate $ complementBit x 30
    | otherwise = complementBit x 30

Nó dễ dàng hơn nhiều trong một ngôn ngữ động (Python). Chỉ cần kiểm tra xem đối số có phải là số X không và trả về lambda trả về -X:

def f(x):
   if isinstance(x,int):
      return (lambda: -x)
   else:
      return x()

23
Thật tuyệt, tôi thích điều này ... cách tiếp cận tương tự trong JavaScript: var f = function (n) {return (typeof n == 'function')? n (): function () {return -n; }}
Đánh dấu Renouf

Có lẽ chỉ là Haskell của tôi rất gỉ, nhưng bạn đã kiểm tra xem (f 0) chưa? Có vẻ như điều đó sẽ tạo ra kết quả tương tự như (f 0x80000000), ít nhất là nếu chúng ta đang xử lý các số nguyên 32 bit với số học tổng hợp (trên phép toán phủ định). Và điều đó sẽ rất tệ.
Darius Bacon

11
Người phỏng vấn trung bình thậm chí sẽ biết cấu trúc lambda gì?
Jeremy Powell

4
Tất nhiên, một mánh gian lận kiểu như vậy cũng hoạt động trong Haskell, mặc dù nó là tĩnh : class C a b | a->b where { f :: a->b }; instance C Int (()->Int) where { f=const.negate }; instance C (()->Int) Int where { f=($()) }.
leftaroundabout 16/03/13

4
Gì? Trường hợp bạn có ý tưởng rằng typeof f (n) === 'hàm', đặc biệt, trong đó n là một số và bạn mong đợi một số được trả về? Tôi không hiểu làm thế nào một trường hợp có thể áp dụng ở đây. Tôi không nói tiếng Python tốt, nhưng trong đối số kiểm tra JS cho một loại hàm thì hoàn toàn sai trong trường hợp này. Chỉ có giải pháp số áp dụng ở đây. f là một hàm, f (n) là số.
Harry

284

Đây là một bằng chứng về lý do tại sao một hàm như vậy không thể tồn tại, đối với tất cả các số, nếu nó không sử dụng thêm thông tin (ngoại trừ 32 bit của int):

Ta phải có f (0) = 0. (Chứng minh: Giả sử f (0) = x. Sau đó f (x) = f (f (0)) = -0 = 0. Bây giờ, -x = f (f (x (x (x) )) = f (0) = x, có nghĩa là x = 0.)

Hơn nữa, cho bất kỳ xy, giả sử f(x) = y. Chúng tôi muốn f(y) = -xsau đó. Và f(f(y)) = -y => f(-x) = -y. Để tóm tắt: nếu f(x) = y, sau đó f(-x) = -y, và f(y) = -x, và f(-y) = x.

Vì vậy, chúng ta cần chia tất cả các số nguyên trừ 0 thành các bộ 4, nhưng chúng ta có một số lẻ các số nguyên như vậy; không chỉ vậy, nếu chúng ta loại bỏ số nguyên không có đối tác dương, chúng ta vẫn có 2 (mod4).

Nếu chúng ta loại bỏ 2 số tối đa còn lại (theo giá trị abs), chúng ta có thể nhận được hàm:

int sign(int n)
{
    if(n>0)
        return 1;
    else 
        return -1;
}

int f(int n)
{
    if(n==0) return 0;
    switch(abs(n)%2)
    {
        case 1:
             return sign(n)*(abs(n)+1);
        case 0:
             return -sign(n)*(abs(n)-1);
    }
}   

Tất nhiên, một lựa chọn khác, là không tuân thủ 0 và lấy 2 số mà chúng tôi đã xóa làm phần thưởng. (Nhưng đó chỉ là một sự ngớ ngẩn nếu.)


29
Tôi không thể tin rằng tôi đã phải đọc đến đây để tìm ra một giải pháp thủ tục tốt để xử lý các số âm mà không cần dùng đến các biến toàn cầu hoặc các thủ thuật làm xáo trộn mã. Nếu tôi có thể bầu bạn nhiều hơn một lần, tôi sẽ làm thế.
Kyle Simek

Quan sát rất hay, có một số lẻ các số nguyên khác không trong bất kỳ n bit nào được ký.
Andres Jaan Tack

Đây cũng là câu trả lời của tôi, nhưng hãy cẩn thận với trường hợp cạnh n = -2147483648(giá trị tối thiểu); bạn không thể abs(n)trong trường hợp đó và kết quả sẽ không được xác định (hoặc một ngoại lệ).
Kirk Broadhurst

1
@ a1kmm: Xin lỗi, -2³² ở trên phải là -2³¹. Dù sao, trường hợp f (0) 0 (và vì vậy f (0) = - 2³¹) thực sự là trường hợp dễ dàng hơn, vì chúng tôi cho thấy hai cái này bị ngắt kết nối với phần còn lại. Một trường hợp khác chúng ta cần xem xét là f (0) = 0, nhưng f (x) = - 2³¹ với một số x ≠ 0, x ≠ -2³¹. Trong trường hợp đó, f (-2³¹) = f (f (x)) = - x (lưu ý -x không thể là -2³¹, vì không tồn tại x như vậy). Hơn nữa hãy để f (-x) = y. Khi đó f (y) = f (f (-x)) = x. Một lần nữa y không thể là -2³¹ (vì f (y) = x, nhưng f (-2³¹) = - x và x không phải là 0). Vì vậy, -2³¹ = f (x) = f (f (y)) = - y, điều này là không thể. Vì vậy, thực sự 0 và -2³¹ phải được ngắt kết nối với phần còn lại (không phải hình ảnh của bất cứ điều gì khác).
ShreevatsaR

1
@will Không có số 0 nào được ký, nếu (như tôi giả sử) chúng ta đang nói về hai số nguyên 32 bit bổ sung của hai.
goffrie

146

Nhờ quá tải trong C ++:

double f(int var)
{
 return double(var);
} 

int f(double var)
{
 return -int(var);
}

int main(){
int n(42);
std::cout<<f(f(n));
}

4
Thật không may, vì xáo trộn tên, các hàm bạn gọi là "f" thực sự có tên lạ hơn.
pyon

1
Tôi đã nghĩ về một cái gì đó như thế, nhưng nghĩ trong C, điều này đã bị ném đi ... công việc tốt!
Liran Orevi

@Rui Craverio: Nó sẽ không hoạt động trong .NET 3.5+ vì tác giả đã chọn sử dụng từ khóa var làm tên biến.
Kredns

72
về mặt kỹ thuật ... đây không phải là những gì câu hỏi yêu cầu. bạn đã xác định 2 hàm f (), f (int) và f (float) và các câu hỏi hỏi "Thiết kế hàm f () ..."
elcuco

2
@elcuco Về mặt kỹ thuật, tất nhiên, nhưng về mặt logic, đó là một chức năng có nhiều quá tải (bạn có thể thực hiện f (f (42)) với điều đó). Vì định nghĩa không nói gì về tham số và giá trị trả về, tôi khó có thể chấp nhận nó như một định nghĩa kỹ thuật.
Marek Toman

135

Hoặc, bạn có thể lạm dụng bộ tiền xử lý:

#define f(n) (f##n)
#define ff(n) -n

int main()
{
  int n = -42;
  cout << "f(f(" << n << ")) = " << f(f(n)) << endl;
}

Vậy bạn sẽ là Konrad "Le Chiffre" Rudolph chứ? Tôi sẽ lấy áo khoác. Vâng, tôi biết về toàn bộ điều "void main", nhưng thêm "return 0;" chỉ là quá nhiều nỗ lực ;-)
Skizz

25
@Skizz, trả về 0 từ chính không bắt buộc trong c ++ ngay cả với giá trị trả về int ... vì vậy bằng cách thực hiện đúng, bạn thực sự nhập một ký tự ít hơn!
Dan Olson

10
Skizz luôn lạm dụng tiền xử lý: D
Arnis Lapsa

23
Đây không phải là một chức năng .. vì vậy đây không phải là một giải pháp hợp lệ
smerlin 18/03/2016

3
@smerlin: Về mặt kỹ thuật, đây là một hàm nội tuyến trả về một hàm nội tuyến: phần thân của cả hai được mở rộng tại thời điểm biên dịch, hay đúng hơn là trước đó. Không thể có được hiệu quả hơn nhiều so với điều này.
Jon Purdy

103

Điều này đúng với tất cả các số âm.

    f (n) = abs (n)

Bởi vì có một số âm nhiều hơn số dương cho các số nguyên bổ sung twos, nên f(n) = abs(n)có giá trị cho một trường hợp nhiều hơn so với f(n) = n > 0 ? -n : ngiải pháp giống như f(n) = -abs(n). Có bạn bởi một ...: D

CẬP NHẬT

Không, nó không hợp lệ cho một trường hợp nữa vì tôi vừa nhận ra bằng nhận xét của litb ... abs(Int.Min)sẽ chỉ tràn ...

Tôi cũng nghĩ về việc sử dụng thông tin mod 2, nhưng kết luận, nó không hoạt động ... sớm. Nếu được thực hiện đúng, nó sẽ hoạt động cho tất cả các số trừ Int.Minvì điều này sẽ tràn.

CẬP NHẬT

Tôi đã chơi với nó một lúc, tìm kiếm một mẹo thao tác bit đẹp, nhưng tôi không thể tìm thấy một lớp lót đẹp, trong khi giải pháp mod 2 phù hợp với một.

    f (n) = 2n (abs (n)% 2) - n + sgn (n)

Trong C #, điều này trở thành như sau:

public static Int32 f(Int32 n)
{
    return 2 * n * (Math.Abs(n) % 2) - n + Math.Sign(n);
}

Để làm cho nó làm việc cho tất cả các giá trị, bạn phải thay thế Math.Abs()với (n > 0) ? +n : -nvà bao gồm việc tính toán trong một uncheckedkhối. Sau đó, bạn thậm chí Int.Minđược ánh xạ tới chính nó như phủ định không được kiểm soát.

CẬP NHẬT

Lấy cảm hứng từ một câu trả lời khác tôi sẽ giải thích cách thức hoạt động của chức năng và cách xây dựng một chức năng như vậy.

Hãy bắt đầu ngay từ đầu. Hàm fnày được lặp đi lặp lại cho một giá trị nhất định nmang lại một chuỗi các giá trị.

    n => f (n) => f (f (n)) => f (f (f (n))) => f (f (f (f (n)))) => ...

Câu hỏi đòi hỏi f(f(n)) = -n, đó là hai ứng dụng liên tiếp fphủ định lập luận. Hai ứng dụng nữa của f- tổng cộng bốn - phủ nhận lại lập luận một nlần nữa.

    n => f (n) => -n => f (f (f (n))) => n => f (n) => ...

Bây giờ có một chu kỳ rõ ràng của chiều dài bốn. Thay thế x = f(n)và lưu ý rằng phương trình thu được f(f(f(n))) = f(f(x)) = -xgiữ, mang lại kết quả như sau.

    n => x => -n => -x => n => ...

Vì vậy, chúng ta có được một chu kỳ có độ dài bốn với hai số và hai số bị phủ định. Nếu bạn tưởng tượng chu trình là một hình chữ nhật, các giá trị phủ định được đặt ở các góc đối diện.

Một trong nhiều giải pháp để xây dựng một chu trình như vậy là sau đây bắt đầu từ n.

 n => phủ định và trừ đi một
-n - 1 = - (n + 1) => thêm một
-n => phủ nhận và thêm một
 n + 1 => trừ đi một
 n

Một ví dụ cụ thể là của một chu kỳ như vậy +1 => -2 => -1 => +2 => +1. Chúng ta đang gần hoàn tất. Lưu ý rằng chu trình được xây dựng chứa một số dương lẻ, kế tiếp của nó và cả hai số đều phủ định, chúng ta có thể dễ dàng phân chia các số nguyên thành nhiều chu kỳ như vậy ( 2^32là bội số của bốn) và đã tìm thấy một hàm thỏa mãn các điều kiện.

Nhưng chúng tôi có một vấn đề với số không. Chu trình phải chứa 0 => x => 0bởi vì số 0 được phủ định cho chính nó. Và bởi vì các trạng thái chu kỳ đã 0 => xtheo sau nó 0 => x => 0 => x. Đây chỉ là một chu kỳ có độ dài hai và xđược biến thành chính nó sau hai ứng dụng, không phải vào -x. May mắn thay, có một trường hợp giải quyết vấn đề. Nếu Xbằng 0, chúng ta có được một chu kỳ có độ dài chỉ bằng 0 và chúng ta đã giải quyết vấn đề đó kết luận rằng số 0 là một điểm cố định f.

Làm xong? Hầu hết. Chúng ta có các 2^32số, số 0 là một điểm cố định để lại các 2^32 - 1số và chúng ta phải phân chia số đó thành các chu kỳ gồm bốn số. Xấu 2^32 - 1không phải là bội số của bốn - sẽ vẫn còn ba số không thuộc bất kỳ chu kỳ nào có độ dài bốn.

Tôi sẽ giải thích phần còn lại của giải pháp bằng cách sử dụng tập hợp nhỏ hơn 3 bit có chữ ký khác nhau từ -4đến +3. Chúng tôi được thực hiện với số không. Chúng tôi có một chu kỳ hoàn chỉnh +1 => -2 => -1 => +2 => +1. Bây giờ hãy để chúng tôi xây dựng chu kỳ bắt đầu từ +3.

    +3 => -4 => -3 => +4 => +3

Vấn đề phát sinh +4là không thể biểu diễn dưới dạng số nguyên 3 bit. Chúng tôi sẽ có được +4bằng cách phủ nhận -3đến +3- những gì vẫn còn là một chút 3 số nguyên hợp lệ - nhưng sau đó thêm một đến +3(nhị phân 011) mang 100nhị phân. Được hiểu là số nguyên không dấu +4nhưng chúng ta phải hiểu nó là số nguyên đã ký -4. Vì vậy, thực sự -4cho ví dụ này hoặc Int.MinValuetrong trường hợp chung là một điểm cố định thứ hai của phủ định số học số nguyên - 0Int.MinValueđược ánh xạ tới themselve. Vì vậy, chu kỳ thực sự là như sau.

    +3 => -4 => -3 => -4 => -3

Đó là một chu kỳ có độ dài hai và thêm +3vào chu kỳ thông qua -4. Do đó, -4ánh xạ chính xác đến chính nó sau hai ứng dụng chức năng, +3được ánh xạ chính xác đến -3sau hai ứng dụng chức năng, nhưng -3được ánh xạ nhầm sang chính nó sau hai ứng dụng chức năng.

Vì vậy, chúng tôi đã xây dựng một hàm hoạt động cho tất cả các số nguyên trừ một số nguyên. Chúng ta có thể làm tốt hơn không? Không chúng tôi không thể. Tại sao? Chúng ta phải xây dựng các chu kỳ có độ dài bốn và có thể bao phủ toàn bộ phạm vi số nguyên lên đến bốn giá trị. Các giá trị còn lại là hai điểm cố định 0Int.MinValuephải được ánh xạ tới chính chúng và hai số nguyên tùy ý x-xphải được ánh xạ với nhau bởi hai ứng dụng chức năng.

Để ánh xạ xtới -xvà ngược lại, chúng phải tạo thành một chu kỳ bốn và chúng phải được đặt ở các góc đối diện của chu kỳ đó. Trong hậu quả 0Int.MinValuephải ở các góc đối diện, quá. Điều này sẽ ánh xạ chính xác x-xnhưng trao đổi hai điểm cố định 0Int.MinValuesau hai ứng dụng chức năng và để lại cho chúng tôi hai đầu vào không thành công. Vì vậy, không thể xây dựng một hàm hoạt động cho tất cả các giá trị, nhưng chúng ta có một hàm hoạt động cho tất cả các giá trị ngoại trừ một giá trị và đây là cách tốt nhất chúng ta có thể đạt được.


Không đáp ứng các tiêu chí: abs (abs (n))! = -N
Dan Olson

Chắc chắn nó làm, cho tất cả các số âm, như ông nói. Đó là một phần của câu hỏi: Nếu bạn không thể đưa ra một câu hỏi chung, hãy đưa ra một câu hỏi phù hợp với phạm vi rộng nhất có thể.
jalf

Câu trả lời này ít nhất cũng tốt như câu trả lời của Marj Synowiec và Rowland Shaw, nó chỉ hoạt động cho một phạm vi số khác nhau
1800 THÔNG TIN

19
Anh bạn, bạn cũng có thể thoát khỏi "CẬP NHẬT" và chỉ cần viết một câu trả lời đúng. 3/4 dưới ("lấy cảm hứng từ câu trả lời khác") là tuyệt vời.
Andres Jaan Tack

1
Tôi thực sự thích giải pháp abs cho số âm. Đơn giản và dễ hiểu.
Thorbjørn Ravn Andersen

97

Sử dụng các số phức, bạn có thể phân chia hiệu quả nhiệm vụ phủ định một số thành hai bước:

  • nhân n với i và bạn nhận được n * i, được n quay 90 ° ngược chiều kim đồng hồ
  • nhân lại với tôi và bạn nhận được -n

Điều tuyệt vời là bạn không cần bất kỳ mã xử lý đặc biệt nào. Chỉ cần nhân với tôi làm công việc.

Nhưng bạn không được phép sử dụng số phức. Vì vậy, bạn phải bằng cách nào đó tạo trục ảo của riêng mình, sử dụng một phần của phạm vi dữ liệu của bạn. Vì bạn cần chính xác nhiều giá trị tưởng tượng (trung gian) như các giá trị ban đầu, nên bạn chỉ còn lại một nửa phạm vi dữ liệu.

Tôi đã cố gắng hình dung điều này trên hình dưới đây, giả sử dữ liệu 8 bit đã ký. Bạn sẽ phải chia tỷ lệ này cho số nguyên 32 bit. Phạm vi được phép cho n ban đầu là -64 đến +63. Đây là những gì chức năng làm cho tích cực n:

  • Nếu n ở 0..63 (phạm vi ban đầu), lệnh gọi hàm thêm 64, ánh xạ n vào phạm vi 64..127 (phạm vi trung gian)
  • Nếu n nằm trong 64..127 (phạm vi trung gian), hàm sẽ trừ n từ 64, ánh xạ n đến phạm vi 0 ..- 63

Đối với n âm, hàm sử dụng phạm vi trung gian -65 ..- 128.

văn bản thay thế


4
@geschema, bạn đã sử dụng công cụ nào để tạo ra những đồ họa đẹp?
jwfearn

10
Xin lỗi, câu hỏi nói rõ ràng không có số phức.
Rui Craveiro

6
@Liran: Tôi đã sử dụng OmniGraffle (chỉ dành cho máy Mac)
geschema

39
+1 Tôi nghĩ rằng đây là câu trả lời tốt nhất. Tôi không nghĩ mọi người đọc đủ, bởi vì tất cả họ đều lưu ý rằng câu hỏi nói rằng số phức không thể được sử dụng. Tôi đã đọc toàn bộ, và bạn đã mô tả giải pháp bằng số phức để đặt giai đoạn cho giải pháp không phức tạp cho câu hỏi được hỏi. Hoàn thành rất tốt.
jrista

1
@jrista tất cả các giải pháp đều sử dụng thứ nguyên thứ hai, đó là tất cả những 'số phức' thực sự (hầu hết sử dụng lẻ so với chẵn, & trên sử dụng floatso với int). 'Vòng 4 phần tử' mà nhiều câu trả lời mô tả cần có 4 trạng thái, có thể được biểu diễn dưới dạng 2 chiều, mỗi chiều có 2 trạng thái. Các vấn đề với câu trả lời này là nó đòi hỏi phải có thêm không gian chế biến (chỉ 'tác phẩm' cho -64..63, tuy nhiên nhu cầu -128..127 không gian) và không nói rõ ràng công thức bằng văn bản!
Kirk Broadhurst

65

Hoạt động ngoại trừ int.MaxValue và int.MinValue

    public static int f(int x)
    {

        if (x == 0) return 0;

        if ((x % 2) != 0)
            return x * -1 + (-1 *x) / (Math.Abs(x));
        else
            return x - x / (Math.Abs(x));
    }

hình ảnh


Không chắc chắn tại sao điều này đã bị hạ cấp. Đối với những gì đầu vào nó thất bại?
Rodrick Chapman

Tại sao bạn không sử dụng chức năng đăng nhập?!?
comonad

1
Hình ảnh thực sự tốt. Gửi 0đến 0-2147483648đến -2147483648điểm kể từ khi hai con số này là cố định cho các nhà điều hành phủ, x => -x. Đối với phần còn lại của các số, làm theo các mũi tên trong hình trên. Như rõ ràng từ câu trả lời của SurDin và nhận xét của nó, sẽ có hai số, trong trường hợp này 2147483647-2147483647không còn cặp nào khác để trao đổi.
Jeppe Stig Nielsen

Nó trông giống như một nụ cười - với rất nhiều nếp nhăn
Anshul

48

Câu hỏi không nói gì về loại đầu vào và giá trị trả về của hàm fphải là gì (ít nhất không phải là cách bạn đã trình bày) ...

... chỉ là khi n là số nguyên 32 bit thì f(f(n)) = -n

Vì vậy, làm thế nào về một cái gì đó như

Int64 f(Int64 n)
{
    return(n > Int32.MaxValue ? 
        -(n - 4L * Int32.MaxValue):
        n + 4L * Int32.MaxValue);
}

Nếu n là số nguyên 32 bit thì câu lệnh f(f(n)) == -nsẽ đúng.

Rõ ràng, cách tiếp cận này có thể được mở rộng để hoạt động cho một phạm vi số rộng hơn nữa ...


2
Lén lút. Giới hạn ký tự.
Joe Phillips

2
Vâng, tôi đã làm việc trên một cách tiếp cận tương tự. Bạn đánh tôi với nó mặc dù. 1 :)
jalf

1
Rất thông minh! Điều này rất gần với (& hiệu quả giống như) sử dụng các số phức, đây sẽ là giải pháp rõ ràng & lý tưởng nhưng rõ ràng không được phép. Làm việc ngoài phạm vi số lượng cho phép.
Kirk Broadhurst

48

đối với javascript (hoặc các ngôn ngữ được nhập động khác), bạn có thể có chức năng chấp nhận một int hoặc một đối tượng và trả về đối tượng kia. I E

function f(n) {
    if (n.passed) {
        return -n.val;
    } else {
        return {val:n, passed:1};
    }
}

cho

js> f(f(10))  
-10
js> f(f(-10))
10

cách khác, bạn có thể sử dụng quá tải trong một ngôn ngữ được gõ mạnh mặc dù điều đó có thể phá vỡ các quy tắc tức là

int f(long n) {
    return n;
}

long f(int n) {
    return -n;
}

Cái sau không có nghĩa là yêu cầu của hàm "a" (số ít). :)
vẽ

Xóa nửa sau của câu trả lời và đây là một câu trả lời đúng.
jmucchiello

@Drew đó là lý do tại sao tôi đã đề cập rằng nó có thể phá vỡ các quy tắc
cobbal

2
Trong JavaScript, một hàm là một đối tượng và do đó có thể giữ trạng thái.
Nosredna

1
IMO: hàm f (n) {return n.passed? -n.val: {val: n, đã qua: 1}} dễ đọc hơn và ngắn hơn.
Samoody

46

Tùy thuộc vào nền tảng của bạn, một số ngôn ngữ cho phép bạn giữ trạng thái trong chức năng. VB.Net, ví dụ:

Function f(ByVal n As Integer) As Integer
    Static flag As Integer = -1
    flag *= -1

    Return n * flag
End Function

IIRC, C ++ cũng cho phép điều này. Tôi nghi ngờ họ đang tìm kiếm một giải pháp khác nhau mặc dù.

Một ý tưởng khác là vì họ không xác định kết quả của cuộc gọi đầu tiên đến chức năng, bạn có thể sử dụng số lẻ / chẵn để kiểm soát xem có đảo ngược dấu hiệu hay không:

int f(int n)
{
   int sign = n>=0?1:-1;
   if (abs(n)%2 == 0)
      return ((abs(n)+1)*sign * -1;
   else
      return (abs(n)-1)*sign;
}

Thêm một vào độ lớn của tất cả các số chẵn, trừ đi một từ độ lớn của tất cả các số lẻ. Kết quả của hai cuộc gọi có cùng độ lớn, nhưng cuộc gọi mà ngay cả khi chúng ta trao đổi ký hiệu. Có một số trường hợp điều này sẽ không hoạt động (-1, max hoặc min int), nhưng nó hoạt động tốt hơn nhiều so với bất kỳ điều gì khác được đề xuất cho đến nay.


1
Tôi tin rằng nó hoạt động cho MAX_INT vì điều đó luôn kỳ quặc. Nó không hoạt động cho MIN_INT và -1.
Airsource Ltd

9
Nó không phải là một chức năng nếu nó có tác dụng phụ.
số

12
Điều đó có thể đúng trong toán học, nhưng nó không liên quan đến lập trình. Vì vậy, câu hỏi là liệu họ đang tìm kiếm một giải pháp toán học hay một giải pháp lập trình. Nhưng cho rằng đó là cho một công việc lập trình ...
Ryan Lundy

+1 Tôi sẽ đăng một bài trong C với "static int x" đang thực hiện một FIFO với phủ định đầu ra. Nhưng điều này là đủ gần.
phkahler

2
@nos: Vâng, đúng vậy, nó không minh bạch.
Clark Gaebel

26

Khai thác ngoại lệ JavaScript.

function f(n) {
    try {
        return n();
    }
    catch(e) { 
        return function() { return -n; };
    }
}

f(f(0)) => 0

f(f(1)) => -1


Tôi nghi ngờ các ngoại lệ đã được sử dụng như thế này trước đây ... :)
NoBugs

+1 Suy nghĩ ra khỏi hộp. Mát mẻ! Nhưng trong mã sản xuất tôi sẽ sử dụng typeof để an toàn.

21

Đối với tất cả các giá trị 32 bit (với cảnh báo -0 là -2147483648)

int rotate(int x)
{
    static const int split = INT_MAX / 2 + 1;
    static const int negativeSplit = INT_MIN / 2 + 1;

    if (x == INT_MAX)
        return INT_MIN;
    if (x == INT_MIN)
        return x + 1;

    if (x >= split)
        return x + 1 - INT_MIN;
    if (x >= 0)
        return INT_MAX - x;
    if (x >= negativeSplit)
        return INT_MIN - x + 1;
    return split -(negativeSplit - x);
}

Về cơ bản, bạn cần ghép từng vòng lặp -x => x => -x với vòng lặp ay => -y => y. Vì vậy, tôi bắt cặp đối diện của split.

ví dụ: Đối với số nguyên 4 bit:

0 => 7 => -8 => -7 => 0
1 => 6 => -1 => -6 => 1
2 => 5 => -2 => -5 => 2
3 => 4 => -3 => -4 => 3

21

Một phiên bản C ++, có thể bẻ cong các quy tắc một chút nhưng hoạt động cho tất cả các loại số (float, ints, double) và thậm chí các loại lớp làm quá tải phép trừ unary:

template <class T>
struct f_result
{
  T value;
};

template <class T>
f_result <T> f (T n)
{
  f_result <T> result = {n};
  return result;
}

template <class T>
T f (f_result <T> n)
{
  return -n.value;
}

void main (void)
{
  int n = 45;
  cout << "f(f(" << n << ")) = " << f(f(n)) << endl;
  float p = 3.14f;
  cout << "f(f(" << p << ")) = " << f(f(p)) << endl;
}

Ý tưởng tốt. Thay vào đó, bạn có thể mất cấu trúc và thay vào đó có một hàm trả về một con trỏ, hàm kia sẽ vô hiệu hóa và phủ định.
Bắt đầu

20

x86 asm (kiểu AT & T):

; input %edi
; output %eax
; clobbered regs: %ecx, %edx
f:
    testl   %edi, %edi
    je  .zero

    movl    %edi, %eax
    movl    $1, %ecx
    movl    %edi, %edx
    andl    $1, %eax
    addl    %eax, %eax
    subl    %eax, %ecx
    xorl    %eax, %eax
    testl   %edi, %edi
    setg    %al
    shrl    $31, %edx
    subl    %edx, %eax
    imull   %ecx, %eax
    subl    %eax, %edi
    movl    %edi, %eax
    imull   %ecx, %eax
.zero:
    xorl    %eax, %eax
    ret

Đã kiểm tra mã, tất cả các số nguyên 32 bit có thể được thông qua, lỗi với -2147483647 (tràn).


19

Sử dụng toàn cầu ... nhưng vì vậy?

bool done = false
f(int n)
{
  int out = n;
  if(!done)
  {  
      out = n * -1;
      done = true;
   }
   return out;
}

3
Không chắc chắn rằng đây là ý định của người hỏi, nhưng +1 cho "nghĩ ra khỏi hộp".
Liran Orevi

5
Thay vì nói có điều kiện "xong = đúng", bạn nên luôn luôn nói "xong =! Xong", theo cách đó chức năng của bạn có thể được sử dụng nhiều lần.
Chris Lutz

@Chris, vì cài đặt được thực hiện thành đúng nằm trong khối if (! Xong), nên nó tương đương với xong =! Xong, nhưng! Thực hiện không cần phải tính toán (hoặc tối ưu hóa bởi trình biên dịch, nếu nó đủ thông minh) .
nsayer

1
Suy nghĩ đầu tiên của tôi cũng là giải quyết vấn đề này bằng cách sử dụng một biến toàn cục, mặc dù nó có cảm giác như gian lận cho câu hỏi đặc biệt này. Tuy nhiên tôi sẽ lập luận rằng một giải pháp biến toàn cục là giải pháp tốt nhất được đưa ra các thông số kỹ thuật trong câu hỏi. Sử dụng toàn cầu làm cho nó rất dễ hiểu những gì đang xảy ra. Tôi đồng ý rằng một thực hiện! = Thực hiện sẽ tốt hơn mặc dù. Chỉ cần di chuyển bên ngoài mệnh đề if.
Alderath

3
Về mặt kỹ thuật, bất cứ thứ gì duy trì trạng thái không phải là một chức năng, mà là một máy trạng thái. Theo định nghĩa , một hàm luôn cung cấp cùng một đầu ra cho cùng một đầu vào.
Ted Hopp

19

Giải pháp Perl này hoạt động cho số nguyên, số float và chuỗi .

sub f {
    my $n = shift;
    return ref($n) ? -$$n : \$n;
}

Hãy thử một số dữ liệu thử nghiệm.

print $_, ' ', f(f($_)), "\n" for -2, 0, 1, 1.1, -3.3, 'foo' '-bar';

Đầu ra:

-2 2
0 0
1 -1
1.1 -1.1
-3.3 3.3
foo -foo
-bar +bar

Nhưng nó không giữ cho nó một int. Về cơ bản, bạn đang lưu trữ dữ liệu biến toàn cục trong chính int "n" ... ngoại trừ đó không phải là int nếu không bạn không thể làm điều đó. Ví dụ: nếu nlà một chuỗi tôi có thể biến 548 thành "First_Time_548" và sau đó lần sau nó chạy qua hàm ... if (tiền tố == First_Time_ ") thay thế" First_Time_ "bằng" - "
Albert Renshaw

@AlbertRenshaw Không chắc chắn nơi bạn có được những ý tưởng đó. (1) Chắc chắn không có biến toàn cầu liên quan ở đây. (2) Nếu bạn cung cấp cho hàm một int, bạn sẽ nhận được một int trở lại - hoặc một tham chiếu đến một int, nếu bạn gọi hàm đó là số lần lẻ. (3) Có lẽ về cơ bản nhất, đây là Perl . Đối với tất cả các mục đích thực tế, ints và chuỗi hoàn toàn có thể thay thế cho nhau. Các chuỗi giống như số sẽ hoạt động hoàn hảo như số trong hầu hết các ngữ cảnh và các số sẽ vui vẻ xâu chuỗi bất cứ khi nào được hỏi.
FMc

Xin lỗi tôi không biết nhiều về việc có vẻ như bạn đang sử dụng một mảng toàn cầu haha
Albert Renshaw

18

Không ai từng nói f (x) phải cùng loại.

def f(x):
    if type(x) == list:
        return -x[0]
    return [x]


f(2) => [2]
f(f(2)) => -2

16

Tôi thực sự không cố gắng đưa ra giải pháp cho vấn đề, nhưng có một vài ý kiến, vì câu hỏi nêu vấn đề này được đặt ra là một phần của một cuộc phỏng vấn (công việc?):

  • Trước tiên tôi sẽ hỏi "Tại sao một chức năng như vậy lại cần thiết? Vấn đề lớn hơn đây là một phần của cái gì?" thay vì cố gắng giải quyết vấn đề đặt ra thực tế tại chỗ. Điều này cho thấy cách tôi suy nghĩ và cách tôi giải quyết các vấn đề như thế này. Ai biết? Đó thậm chí có thể là lý do thực sự của câu hỏi được hỏi trong một cuộc phỏng vấn ở nơi đầu tiên. Nếu câu trả lời là "Không bao giờ bạn bận tâm, giả sử nó cần thiết và chỉ cho tôi cách bạn sẽ thiết kế chức năng này." Sau đó tôi sẽ tiếp tục làm như vậy.
  • Sau đó, tôi sẽ viết C # test mã tôi sẽ sử dụng (điều hiển nhiên: vòng lặp từ int.MinValueđể int.MaxValue, và đối với từng nở chỗ gọi phạm vi f(f(n))và kiểm tra kết quả là -n), nói sau đó tôi sẽ sử dụng Test Driven Development để có được một chức năng như vậy.
  • Chỉ khi người phỏng vấn tiếp tục yêu cầu tôi giải quyết vấn đề đặt ra, tôi mới thực sự bắt đầu thử và viết nguệch ngoạc trong suốt cuộc phỏng vấn để thử và đưa ra một câu trả lời. Tuy nhiên, tôi không thực sự nghĩ rằng tôi sẽ nhảy việc để nhận công việc nếu người phỏng vấn sẽ là bất kỳ dấu hiệu nào cho thấy công ty giống như thế nào ...

Ồ, câu trả lời này giả định rằng cuộc phỏng vấn dành cho vị trí liên quan đến lập trình C #. Tất nhiên sẽ là một câu trả lời ngớ ngẩn nếu cuộc phỏng vấn dành cho một vị trí liên quan đến toán học. ;-)


7
Bạn thật may mắn khi họ yêu cầu 32 int, nếu là 64 bit, cuộc phỏng vấn sẽ không bao giờ tiếp tục sau khi bạn chạy các bài kiểm tra ;-)
alex2k8

Thật vậy, nếu tôi thậm chí sẽ đi đến một điểm để thực sự viết bài kiểm tra đó và chạy nó trong một cuộc phỏng vấn. ;-) Quan điểm của tôi: Tôi sẽ cố gắng không đi đến điểm đó trong một cuộc phỏng vấn. Theo tôi, lập trình là "cách suy nghĩ" hơn là "cách anh ta viết các dòng mã".
peSHIr

7
Đừng làm theo lời khuyên này trong một cuộc phỏng vấn thực tế. Người phỏng vấn mong bạn thực sự trả lời câu hỏi. Đặt câu hỏi về mức độ liên quan của câu hỏi sẽ không mua cho bạn bất cứ điều gì nhưng nó có thể gây khó chịu cho người phỏng vấn. Thiết kế một bài kiểm tra tầm thường không đưa bạn đến gần hơn với câu trả lời và bạn không thể thực hiện nó trong cuộc phỏng vấn. Nếu bạn có thêm thông tin (32 bit), hãy cố gắng tìm hiểu làm thế nào điều đó có thể hữu ích.
Stefan Haustein

Một người phỏng vấn bị làm phiền khi tôi hỏi thêm thông tin (trong khi có thể đặt câu hỏi về mức độ liên quan của câu hỏi của anh ta trong quá trình) không phải là người phỏng vấn tôi nhất thiết muốn làm việc cho / với. Vì vậy, tôi sẽ tiếp tục đặt câu hỏi như vậy trong các cuộc phỏng vấn. Nếu họ không thích điều đó, có lẽ tôi sẽ kết thúc cuộc phỏng vấn để ngừng lãng phí cả thời gian của chúng tôi nữa. Đừng như "Tôi chỉ làm theo đơn đặt hàng" một chút. Bạn có ..?
peSHIr

16

Tôi sẽ thay đổi 2 bit quan trọng nhất.

00.... => 01.... => 10.....

01.... => 10.... => 11.....

10.... => 11.... => 00.....

11.... => 00.... => 01.....

Như bạn có thể thấy, nó chỉ là một bổ sung, bỏ đi phần mang theo.

Làm thế nào tôi có câu trả lời? Suy nghĩ đầu tiên của tôi chỉ là sự cần thiết cho sự đối xứng. 4 lượt để quay lại nơi tôi bắt đầu. Lúc đầu tôi nghĩ, đó là 2 bit mã Gray. Sau đó, tôi nghĩ rằng thực sự nhị phân tiêu chuẩn là đủ.


Vấn đề với cách tiếp cận này là nó không hoạt động với hai số âm khen ngợi (đó là điều mà mọi CPU hiện đại sử dụng). Đó là lý do tại sao tôi xóa câu trả lời giống hệt của mình.
Tamas Czinege

Câu hỏi chỉ định số nguyên có chữ ký 32 bit. Giải pháp này không hoạt động đối với các biểu diễn bổ sung hai hoặc bổ sung của hai số nguyên có chữ ký 32 bit. Nó sẽ chỉ hoạt động cho các biểu diễn ký hiệu và cường độ, rất không phổ biến trong các máy tính hiện đại (trừ các số dấu phẩy động).
Jeffrey L Whitledge

1
@DrJokepu - Wow, sau sáu tháng - jinx!
Jeffrey L Whitledge

Bạn không chỉ cần chuyển đổi các số thành biểu diễn ký hiệu và cường độ bên trong hàm, thực hiện chuyển đổi, sau đó chuyển đổi trở lại bất kỳ biểu diễn số nguyên gốc nào trước khi trả về nó?
Bill Michell

Tôi thích rằng về cơ bản bạn đã triển khai các số phức bằng cách giới thiệu một bit tưởng tượng :)
jabirali

16

Đây là một giải pháp được lấy cảm hứng từ yêu cầu hoặc tuyên bố rằng các số phức không thể được sử dụng để giải quyết vấn đề này.

Nhân với căn bậc hai của -1 là một ý tưởng, điều đó dường như chỉ thất bại vì -1 không có căn bậc hai trên các số nguyên. Nhưng chơi xung quanh với một chương trình như mathicala chẳng hạn, cho phương trình

(1849436465 2 +1) mod (2 32 -3) = 0.

và điều này gần như là có căn bậc hai của -1. Kết quả của hàm cần phải là một số nguyên đã ký. Do đó tôi sẽ sử dụng một mod hoạt động modulo đã sửa đổi (x, n) trả về số nguyên y đồng dạng với x modulo n gần nhất với 0. Chỉ có rất ít ngôn ngữ lập trình có hoạt động modulo, nhưng có thể dễ dàng xác định nó . Ví dụ, trong python, nó là:

def mods(x, n):
    y = x % n
    if y > n/2: y-= n
    return y

Sử dụng phương trình trên, vấn đề bây giờ có thể được giải quyết như

def f(x):
    return mods(x*1849436465, 2**32-3)

Điều này thỏa mãn f(f(x)) = -xcho tất cả các số nguyên trong phạm vi . Kết quả cũng nằm trong phạm vi này, nhưng tất nhiên việc tính toán sẽ cần các số nguyên 64 bit.[-231-2, 231-2]f(x)


13

C # cho phạm vi 2 ^ 32 - 1 số, tất cả các số int32 ngoại trừ (Int32.MinValue)

    Func<int, int> f = n =>
        n < 0
           ? (n & (1 << 30)) == (1 << 30) ? (n ^ (1 << 30)) : - (n | (1 << 30))
           : (n & (1 << 30)) == (1 << 30) ? -(n ^ (1 << 30)) : (n | (1 << 30));

    Console.WriteLine(f(f(Int32.MinValue + 1))); // -2147483648 + 1
    for (int i = -3; i <= 3  ; i++)
        Console.WriteLine(f(f(i)));
    Console.WriteLine(f(f(Int32.MaxValue))); // 2147483647

in:

2147483647
3
2
1
0
-1
-2
-3
-2147483647

Điều này cũng không hoạt động đối với f (0) là 1073741824. f (1073741824) = 0. f (f (1073741824)) = 1073741824
Dinah

Nói chung, bạn có thể chứng minh rằng đối với loại số nguyên bổ sung của hai kích thước bit bất kỳ, hàm này không hoạt động với ít nhất hai giá trị đầu vào.
kẻ lười biếng

12

Về cơ bản, hàm phải chia phạm vi khả dụng thành các chu kỳ có kích thước 4, với -n ở cuối đối diện của chu kỳ n. Tuy nhiên, 0 phải là một phần của chu kỳ kích thước 1, vì nếu không0->x->0->x != -x . Do 0 ở một mình, nên có 3 giá trị khác trong phạm vi của chúng tôi (có kích thước là bội số của 4) không theo một chu kỳ thích hợp với 4 phần tử.

Tôi chọn những giá trị lạ thêm được MIN_INT, MAX_INT, và MIN_INT+1. Hơn nữa, MIN_INT+1sẽ ánh xạ MAX_INTchính xác, nhưng bị mắc kẹt ở đó và không ánh xạ trở lại. Tôi nghĩ rằng đây là sự thỏa hiệp tốt nhất, bởi vì nó có thuộc tính tốt đẹp chỉ có các giá trị cực đoan không hoạt động chính xác. Ngoài ra, nó có nghĩa là nó sẽ hoạt động cho tất cả các BigInt.

int f(int n):
    if n == 0 or n == MIN_INT or n == MAX_INT: return n
    return ((Math.abs(n) mod 2) * 2 - 1) * n + Math.sign(n)

12

Không ai nói nó phải không quốc tịch.

int32 f(int32 x) {
    static bool idempotent = false;
    if (!idempotent) {
        idempotent = true;
        return -x;
    } else {
        return x;
    }
}

Gian lận, nhưng không nhiều như nhiều ví dụ. Thậm chí còn tệ hơn là nhìn trộm ngăn xếp để xem địa chỉ của người gọi của bạn có phải là & f hay không, nhưng điều này sẽ dễ mang theo hơn (mặc dù không an toàn cho luồng ... phiên bản an toàn luồng sẽ sử dụng TLS). Thậm chí còn ác hơn:

int32 f (int32 x) {
    static int32 answer = -x;
    return answer;
}

Tất nhiên, cả hai cách này đều không hoạt động tốt cho trường hợp MIN_INT32, nhưng có rất ít thứ bạn có thể làm về điều đó trừ khi bạn được phép trả lại loại rộng hơn.


bạn có thể 'nâng cấp' nó để hỏi về địa chỉ (vâng, bạn phải lấy nó bằng ref \ như một con trỏ) - ví dụ: C: int f (int & n) {static int * addr = & n; if (addr == & n) {return -n; } trả về n; }
IUn UnknownPulum

11

Tôi có thể tưởng tượng sử dụng bit thứ 31 như một bit tưởng tượng ( i ) sẽ là một cách tiếp cận hỗ trợ một nửa tổng phạm vi.


Điều này sẽ phức tạp hơn nhưng không hiệu quả hơn câu trả lời tốt nhất hiện tại
1800 THÔNG TIN

1
@ 1800 THÔNG TIN: Mặt khác, tên miền [-2 ^ 30 + 1, 2 ^ 30-1] tiếp giáp nhau, hấp dẫn hơn từ quan điểm toán học.
Jochen Walter

10

hoạt động cho n = [0 .. 2 ^ 31-1]

int f(int n) {
  if (n & (1 << 31)) // highest bit set?
    return -(n & ~(1 << 31)); // return negative of original n
  else
    return n | (1 << 31); // return n with highest bit set
}

10

Vấn đề nêu rõ "số nguyên có chữ ký 32 bit" nhưng không xác định liệu chúng có bổ sung twos không hay bổ sung .

Nếu bạn sử dụng bổ sung cho nhau thì tất cả 2 ^ 32 giá trị xảy ra theo chu kỳ có độ dài bốn - bạn không cần trường hợp đặc biệt bằng 0 và bạn cũng không cần điều kiện.

Trong C:

int32_t f(int32_t x)
{
  return (((x & 0xFFFFU) << 16) | ((x & 0xFFFF0000U) >> 16)) ^ 0xFFFFU;
}

Điều này hoạt động bởi

  1. Trao đổi khối 16 bit cao và thấp
  2. Đảo ngược một trong các khối

Sau hai lần vượt qua, chúng ta có nghịch đảo bit của giá trị ban đầu. Mà trong đại diện bổ sung tương đương với phủ định.

Ví dụ:

Pass |        x
-----+-------------------
   0 | 00000001      (+1)
   1 | 0001FFFF (+131071)
   2 | FFFFFFFE      (-1)
   3 | FFFE0000 (-131071)
   4 | 00000001      (+1)

Pass |        x
-----+-------------------
   0 | 00000000      (+0)
   1 | 0000FFFF  (+65535)
   2 | FFFFFFFF      (-0)
   3 | FFFF0000  (-65535)
   4 | 00000000      (+0)

1
Điều gì về thứ tự byte trên các kiến ​​trúc khác nhau?
Steven

1
Tất cả các số học là 32 bit. Tôi không thao tác các byte riêng lẻ, vì vậy thứ tự byte sẽ không ảnh hưởng đến nó.
Finnw

Điều này nghe khá gần. Bạn có thể giả sử đầu vào là 2 bổ sung. Vì vậy, bạn chuyển đổi để đại diện bit dấu. Bây giờ tùy thuộc vào bit cuối cùng, bạn lật bit đầu tiên và bit cuối cùng hoặc chỉ bit cuối cùng. Về cơ bản, bạn chỉ phủ định số chẵn và chu kỳ chẵn / lẻ mọi lúc. Vì vậy, bạn nhận được từ lẻ đến lẻ ​​và thậm chí đến thậm chí sau 2 cuộc gọi. Cuối cùng, bạn chuyển đổi trở lại thành 2 bổ sung. Đã đăng mã cho điều này ở đâu đó bên dưới.
Stefan Haustein

9

: D

boolean inner = true;

int f(int input) {
   if(inner) {
      inner = false;
      return input;
   } else {
      inner = true;
      return -input;
   }
}

5
Cũng có thể giúp bạn thảo luận về lý do tại sao các biến toàn cầu lại xấu nếu họ không loại bạn ra khỏi cuộc phỏng vấn ngay tại đó!
palswim


7

Tôi muốn chia sẻ quan điểm của tôi về vấn đề thú vị này với tư cách là một nhà toán học. Tôi nghĩ rằng tôi có giải pháp hiệu quả nhất.

Nếu tôi nhớ chính xác, bạn phủ nhận số nguyên 32 bit đã ký bằng cách lật bit đầu tiên. Ví dụ: nếu n = 1001 1101 1110 1011 1110 0000 1110 1010, thì -n = 0001 1101 1110 1011 1110 0000 1110 1010.

Vậy làm thế nào để chúng ta xác định hàm f lấy số nguyên 32 bit đã ký và trả về một số nguyên 32 bit đã ký khác với thuộc tính lấy f hai lần giống như lật bit đầu tiên?

Hãy để tôi viết lại câu hỏi mà không đề cập đến các khái niệm số học như số nguyên.

Làm thế nào để chúng ta xác định một hàm f lấy một chuỗi các số 0 và một số có độ dài 32 và trả về một chuỗi các số không và các số có cùng độ dài, với thuộc tính lấy f hai lần giống như lật bit đầu tiên?

Quan sát: Nếu bạn có thể trả lời câu hỏi trên cho trường hợp 32 bit, thì bạn cũng có thể trả lời cho trường hợp 64 bit, trường hợp 100 bit, v.v. Bạn chỉ cần áp dụng f cho 32 bit đầu tiên.

Bây giờ nếu bạn có thể trả lời câu hỏi cho trường hợp 2 bit, Voila!

Và vâng, hóa ra 2 bit đầu tiên là đủ.

Đây là mã giả

1. take n, which is a signed 32-bit integer.
2. swap the first bit and the second bit.
3. flip the first bit.
4. return the result.

Ghi chú: Bước 2 và bước 3 cùng nhau có thể được mùa hè là (a, b) -> (-b, a). Trông quen quen? Điều đó sẽ nhắc nhở bạn về góc quay 90 độ của mặt phẳng và phép nhân với căn bậc hai của -1.

Nếu tôi chỉ trình bày mã giả một mình mà không có khúc dạo đầu dài, nó có vẻ giống như một con thỏ ra khỏi mũ, tôi muốn giải thích làm thế nào tôi có được giải pháp.


6
Vâng, đó là một vấn đề thú vị. Bạn biết toán của bạn. Nhưng đây là một vấn đề khoa học máy tính. Vì vậy, bạn cần phải học máy tính. Đại diện cho cường độ ký hiệu được cho phép nhưng nó đã lỗi thời khoảng 60 năm trước. Bổ sung của 2 là phổ biến nhất.
Lập trình viên Windows

5
Đây là chức năng của bạn làm gì với hai bit khi được áp dụng hai lần: (a, b) -> (-b, a) -> (-a, -b). Nhưng, chúng tôi đang cố gắng để đến (-a, b), chứ không phải (-a, -b).
buti-oxa

@ buti-oxa, bạn nói đúng. Thao tác hai bit sẽ diễn ra như sau: 00 -> 01 -> 10 -> 11 -> 00. Nhưng sau đó thuật toán của tôi giả sử biểu diễn cường độ ký hiệu không phổ biến hiện nay, như lập trình viên Windows đã nói, vì vậy tôi nghĩ thuật toán của tôi ít được sử dụng .
Yoo

Vì vậy, anh ta không thể làm các bước hai lần thay vì một lần?
Nosredna

4
buti-oxa hoàn toàn đúng: hàm thậm chí không lật được bit đầu tiên sau hai lần gọi, nó lật hai bit đầu tiên. Lật tất cả các bit gần hơn với phần bổ sung của 2, nhưng nó không chính xác.
redtuna
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.