So sánh hai số nguyên trong C hoặc C ++ mà không cần toán tử so sánh


12

Tạo chương trình ngắn nhất lấy hai số nguyên đã ký làm đầu vào (thông qua stdin hoặc làm đối số) và hiển thị 3 đầu ra khác nhau tùy thuộc vào số đầu tiên có (1) lớn hơn, (2) nhỏ hơn, hoặc (3) bằng số thứ hai con số.

Cuộc đuổi bắt

Bạn không thể sử dụng bất kỳ điều nào sau đây trong chương trình của mình:

  • Các toán tử so sánh tiêu chuẩn: <, >, <=, >=, ==, !=.
  • Bất kỳ tập tin thư viện ngoài conio, stdiohoặc iostream.
  • Bất kỳ ký tự ASCII không in hoặc không in được.

Người chiến thắng

Chương trình có số lượng nhân vật ngắn nhất sẽ thắng.


Tôi cho rằng sử dụng những thứ như abs không bao gồm tệp thư viện (vì trình biên dịch biết dù sao đi nữa) cũng không được phép?
Martin Ender

1
@ MartinBüttner có, đó sẽ là một giả định chính xác. :)
lùm cây

5
Tại sao hạn chế đối với C (++)? Nếu đó là vì bạn muốn câu trả lời có thể mang theo được mặc dù không thể truy cập được các loại cơ bản của C thì bạn nên nói rõ điều đó. Nếu đó là một hạn chế tùy ý thì bạn nên lưu ý rằng các hạn chế tùy ý đối với một ngôn ngữ là không phổ biến trên trang web này.
Peter Taylor

8
@PeterTaylor đó là một phần của thử thách. Nó sẽ là một trò chơi bóng rất khác nhau hoàn toàn nếu câu hỏi không liên quan đến ngôn ngữ. Việc giới hạn nó với C / C ++ sẽ thay đổi các chiến lược được sử dụng khi tiếp cận vấn đề. Tôi nhận thấy sự cần thiết phải đặt câu hỏi cho hầu hết các ngôn ngữ để thúc đẩy sự tham gia của nhiều người hơn, nhưng trong vấn đề cụ thể này, việc hạn chế C / C ++ và các toán tử và phương thức cụ thể của họ là một phần không thể thiếu của thách thức.
lùm cây

1
@EvilTeach có; nếu bất cứ điều gì không bị cấm rõ ràng trong câu hỏi, thì nó được cho phép.
lùm cây

Câu trả lời:


2

53 byte

main(a,b){scanf("%d%d",&a,&b);printf("%ld",0l+a-b);}

Chỉ có ký tự đầu tiên của đầu ra là có liên quan. Ba đầu ra khác nhau là:

  1. '-' nếu b> a
  2. '0' nếu a == b
  3. bất kỳ ký tự nào khác nếu a> b

Nó hoạt động cho phạm vi đầu vào đầy đủ của int trên tất cả các nền tảng trong đó sizeof (dài)> sizeof (int).

Chỉnh sửa: thay vào đó, chi phí thêm một ký tự để tạo trường hợp 3 in một dấu '+' duy nhất:

main(a,b){scanf("%d%d",&a,&b);printf("%+ld",0l+a-b);}

6

Có lẽ tôi đang thiếu một cái gì đó trong các quy tắc, nhưng ...

81 byte

main(a,b){scanf("%d%d",&a,&b);long long l=a;l-=b;printf("%lld%d",--l>>63,l>>63);}

Ouputs 00nếu a > b, -10nếu a == b-1-1nếu a < b.


Phổ biến như loại mã này, C không đảm bảo nó hoạt động. long longcó thể nhiều hơn 64 bit, intcó thể lớn đến mức bạn có thể tràn, kết quả của các giá trị âm thay đổi phải được thực hiện được xác định. Khá nhiều câu trả lời có nguồn gốc C có vấn đề tương tự.
Yann Vernier

1
@YannVernier: Đã hiểu. Tôi đoán rằng một giải pháp hoàn hảo 100% sẽ là một con quái vật, vì điều duy nhất chúng ta có thể làm một cách an toàn (tức là không bị dịch chuyển hoặc tràn) là một chút, và để làm điều đó một cách an toàn, chúng ta cần xác định độ dài của toán hạng bằng cách sử dụng sizeof.
COTO

6

90 byte

Nếu chúng ta có thể sử dụng stdio, tại sao không sử dụng khả năng định dạng của nó để thực hiện so sánh?

main(a,b){scanf("%d%d",&a,&b);snprintf(&a,2,"%d",b-a);a&=63;putchar(51-!(a-45)-!!(a-48));}

Giả sử mã hóa tương thích ASCII và độ bền nhỏ.

72 byte

Các chỉ số được làm tròn về 0 nhưng các ca phải là (trong thực tế) "làm tròn xuống". Đó là một tặng cho chết.

main(a,b){scanf("%d%d",&a,&b);a-=b;putchar(a?a|=1,a/2-(a>>1)?60:62:61);}

65 79 byte

Một đặc tính khác biệt của số âm là chúng tạo ra modulo âm. Điều này không phụ thuộc vào đại diện số nguyên; nó thậm chí hoạt động trên máy nướng bánh mì vượt quá 8 bit của tôi! Ồ, và vì chúng ta có thể sử dụng conio, tại sao không lưu hai byte với putch? Bây giờ, nếu tôi chỉ có thể tìm thấy bản sao TurboC của mình ...

main(a,b){scanf("%d%d",&a,&b);long long d=a;d-=b;putch(d?d|=1,d%2-1?60:62:61);}

EDIT : Xử lý sự khác biệt lớn giả định long longlà rộng hơn int.


Tôi khá chắc chắn rằng bạn cần một dấu phân cách giữa các %ds scanfđể phân tích rõ ràng hai số nguyên. Ý tưởng tốt đẹp mặc dù!
Martin Ender

1
@Martin: Chà, nó hoạt động với GCC, nhưng tôi thực sự không chắc đó có phải là sự thật không.
Ell

Ý tôi là, làm thế nào để bạn phân biệt giữa đầu vào a = 1, b = 23a = 12, b = 3. Bạn có cần phải đặt 123STDIN trong cả hai trường hợp không?
Martin Ender

1
Như tôi đã nói, nó dường như hoạt động (với 1 2312 3là đầu vào).
Ell

2
Ohhh, bạn bao gồm không gian trong đầu vào. Vâng, tôi không ngạc nhiên khi nó hoạt động.
Martin Ender

5

64 61 ký tự

main(a,b){scanf("%d%d",&a,&b);for(a-=b;a/2;a/=2);putchar(a);}

In các giá trị ký tự của -1, 0 và 1 với giá trị nhỏ hơn, bằng hoặc lớn hơn, tương ứng.

Thực hiện này dựa trên hành vi undefined cho blà của loại intvà cho đầu vào bên ngoài phạm vi INT_MIN / 2để INT_MAX / 2. Trên các nền tảng nơi tràn tràn ký kết bao quanh, cho dù bổ sung 2 giây (về cơ bản là tất cả chúng) hoặc cường độ ký hiệu, nó sẽ thất bại trong 25% các cặp hợp lệ có thể int. Thật thú vị (với tôi dù thế nào), nó sẽ hoạt động chính xác trên các nền tảng nơi bão hòa đã ký.


Điều này sẽ không hoạt động nếu a-btràn.
Dennis

Đáng buồn thay, đó là sự thật, nhưng tôi không thể nghĩ ra bất kỳ cách thức bất khả tri nào để tránh điều này mà không có toán tử so sánh. Câu hỏi không chỉ định một phạm vi đầu vào mà kết quả phải hợp lệ. Câu trả lời này được đảm bảo bởi tiêu chuẩn để hoạt động cho tất cả các đầu vào giữa -(2^14)2^14 - 1trên tất cả các nền tảng tuân thủ và có thể nó sẽ hoạt động cho một phạm vi lớn hơn đáng kể trên hầu hết các nền tảng. Tất cả các câu trả lời khác tại thời điểm này đưa ra các giả định về kích thước của loại, kích thước tương đối của loại hoặc đại diện.
laindir

Câu hỏi cho biết hai số nguyên đã ký làm đầu vào , vì vậy tôi muốn nói rằng nó phải hoạt động cho tất cả các cặp. main(a,b)đã không xác định hành vi, vì vậy không có câu trả lời nào được đảm bảo để hoạt động. Nevermind tính di động.
Dennis

Bạn hoàn toàn đúng về hành vi không xác định, vì vậy việc triển khai của tôi thực sự không đảm bảo bất cứ điều gì theo tiêu chuẩn. Tôi sẽ thêm một ghi chú cho biết những hạn chế của nó là gì.
laindir

3

66 102 byte

main(a,b,c,d,e){scanf("%d %d",&a,&b);e=1<<31;c=a&e;d=b&e;putchar(a-b?c&~d?48:d&~c?49:a-b&e?48:49:50);}

Đọc các số nguyên từ STDIN và in 0(a <b), 1(a> b) hoặc 2(a == b).

Chỉnh sửa: Bây giờ nó cũng sẽ hoạt động cho các khác biệt quá lớn để phù hợp với số nguyên 32 bit. Tôi chắc chắn rằng chim nhạn lồng nhau có thể được rút ngắn với một số phép thuật bit hơn.


Chỉnh sửa cho tôi nếu tôi sai, nhưng tôi đang nhìn thấy <0 trong phần bên trong của bạn.
overactor

@overactor đã sửa
Martin Ender

3

52 byte

Đáng buồn là cái này chỉ hoạt động cho các số nguyên dương, nhưng tôi nghĩ khái niệm sử dụng các toán tử số học thuần túy là thú vị:

main(a,b){scanf("%d%d",&a,&b);putchar(b%a/b-a%b/a);}

Đầu ra:

  • Mã ASCII 0xFF: ít hơn b
  • Mã ASCII 0x00: a bằng b
  • Mã ASCII 0x01: lớn hơn b

Nếu bạn chỉ dành cho số nguyên dương, putchar(a/b-b/a)thì ngắn hơn rất nhiều.
Dennis

@Dennis tạo ra các đầu ra khác nhau, ví dụ cho (50,1) và (51,1). Nhưng tôi đã có thể rút ngắn một chút mặc dù.
Chấn thương kỹ thuật số

1
Phải, đã không suy nghĩ đúng đắn ...
Dennis

3

 59    54 ký tự

54 charcters với trình biên dịch như gcc không hoạt động tại main(x,y):

main(x,y){scanf("%d%d",&x,&y);y-=x;putchar(y>>31|!y);}

59 ký tự khác:

main(){int x,y;scanf("%d%d",&x,&y);y-=x;putchar(y>>31|!y);}

Đầu ra:

  • Mã ASCII 0x00 nếu x <y
  • Mã ASCII 0xFF nếu x> y
  • Mã ASCII 0x01 nếu x == y

1
Tôi có thể đảm bảo với bạn, main(x,y)hoạt động trong gcc, vì vậy hãy thoải mái thả 5 byte đó khỏi số ký tự của bạn.
Martin Ender

main (x, y) không hoạt động trên gcc của tôi. Có lẽ một tùy chọn trình biên dịch là bắt buộc. Nhưng bạn có thể thay thế main (x, y) bằng x; main (y).
Florian F

2

66 byte

main(a,b){scanf("%d%d",&a,&b);putchar((0l+b-a>>63)-(0l+a-b>>63));}

In byte 0x00 nếu a == b, 0x01 nếu a < bvà 0xff nếua > b .

ký tự ASCII không in hoặc không thể in trong chương trình [của tôi]nếu bất cứ điều gì không bị cấm rõ ràng trong câu hỏi, thì nó được phép , ký tự không thể in trong đầu ra sẽ hoàn toàn ổn.


Phiên bản trước của tôi đã không xử lý tràn rất tốt. Điều này hoạt động trên x64 Linux, trong đó long64 bit.
Dennis

2

87 ký tự

main(a,b,c){scanf("%d%d",&a,&b);c=1<<31;a+=c;b+=c;puts(a^b?(unsigned)a/b?">":"<":"=");}

Sử dụng thủ thuật 2 ^ 31 để chuyển đổi thành int unsign

Truyền phân chia thành không dấu để xử lý bit trên dưới dạng dữ liệu, không ký

Sử dụng ^ đến XOR a và b, khi chúng bằng nhau, trả về 0

Sử dụng các điều kiện lồng nhau (?) Để lấy "<", ">" hoặc "=" để cung cấp cho đặt ()


1

71 byte

main(x,y,z){scanf("%d%d",&x,&y);putchar((z=x-y)?(z&(z>>31))?50:49:51);}

http://ideone.com/uvXm6c


Ideone của bạn có dấu ngoặc đơn xung quanh z=x-yvà tôi khá chắc chắn rằng chúng là cần thiết. Bạn cũng có thể lưu hai ký tự bằng cách sử dụng 49, 50` và 51trực tiếp, thay vì thêm 48.
Martin Ender

Chuyển nhượng có quyền ưu tiên thấp hơn toán tử ternary: en.cppreference.com/w/c/lingu/operator_precedence
Martin Ender

Mã của bạn ở trên thiếu dấu chấm phẩy và không thành công -2000000000 2000000000, cũng như bất kỳ tổ hợp số nguyên nào khác gây ra lỗi tràn trong phép trừ.
COTO

1

68 ký tự

int main(a,b){scanf("%d%d",&a,&b);putchar(a-b?((unsigned)a-b)>>31:2);}

Đặt ký tự ASCII 1, 2 hoặc 3 với giá trị nhỏ hơn, lớn hơn hoặc bằng, tương ứng.


1
Điều này sẽ không hoạt động nếu a-btràn.
Dennis

1

88 89 byte

main(a,b){scanf("%d%d",&a,&b);a+=1<<31;b+=1<<31;for(;a&&b;a--)b--;putchar(a?b?48:49:50);}

Điều này bắt đầu bằng cách thêm 1<<31( INT_MIN) vào a và b, để 0 bây giờ tương ứngINT_MIN . Sau đó, các vòng lặp và giảm a và b mỗi vòng lặp cho đến khi bằng 0, sau đó in 0, 1 hoặc 2 tùy thuộc vào việc a, b hoặc cả hai đều bằng 0.

120 119 byte

main(a,b,c){scanf("%d%d",&a,&b);c=1<<31;a^=c;b^=c;for(c~=c;!c;c/=2)if(a&c^b&c){putchar(a?48:49);return;}putchar(50);}

Đó không phải là giải pháp ngắn nhất vì nó có thể được chơi golf một chút bởi người chơi golf giỏi hơn tôi. (Hoặc chỉ những người có kiến ​​thức về C nhiều hơn tôi)

Ý tưởng là che dấu từng bit, bắt đầu từ bên trái và kiểm tra sự bất bình đẳng. Phần còn lại nên tự giải thích. Vì các số âm bắt đầu bằng 1 bit, trước tiên tôi đảo ngược bit đầu tiên bằng a^=1<<31.


Tôi không thể kiểm tra các giải pháp của mình vào lúc này, vì vậy hãy thoải mái chỉ ra những sai lầm.
overactor

Giải pháp đầu tiên có một vài vấn đề: 1. ;)Nụ cười vui vẻ nên là một );nụ cười buồn . 2. a&bchỉ kiểm tra nếu abcó bit chung; bạn cần &&.
Dennis

@Dennis, bạn nói đúng, cảm ơn.
overactor

1

Tôi nghĩ tôi thậm chí sẽ không thử viết mã ngắn. Những gì tôi sẽ cố gắng là thực hiện so sánh này theo cách có thể di động theo thông số C99.

int a, b;   // Let's assume these are initialized
int sign_a = a ? ((a|7)^2)%2 + ((a|7)^3)%2 : 0;

Toán tử modulo bảo toàn dấu, nhưng nó cũng có thể tạo ra số 0 (bao gồm cả số 0 âm), vì vậy chúng tôi đảm bảo chúng tôi có cả giá trị lẻ và giá trị chẵn để kiểm tra (ngay cả khi không biết liệu chúng tôi có đang sử dụng bổ sung không). Các phép toán số học có thể tràn, nhưng bitwise sẽ không, và bằng cách đảm bảo có các bit được đặt và xóa, chúng ta tránh vô tình chuyển số của mình thành số 0 âm hoặc giá trị bẫy. Thực tế là hai thao tác được yêu cầu thực hiện một cách kỳ quặc không quan trọng, bởi vì biểu diễn bẫy có thể không gây ra hành vi không xác định cho đến khi đưa vào một giá trị. Thực hiện thao tác với bit 0 được bật, đảm bảo chúng ta nhận được chính xác một phần còn lại khác không. Được trang bị kiến ​​thức về cả hai dấu hiệu, chúng ta có thể quyết định cách tiến hành so sánh.

char result="\0<<>=<>>\0"[4+3*sign_a+sign_b]
if (!result) {   // signs matching means subtraction won't overflow
  int diff=a-b;
  int sign_diff=diff ? (diff|7^2)%2 + (diff|7^3)%2 : 0;
  result = ">=<"[1-sign_diff];
}

Phương pháp này có thể là một trong số ít cho phép trích xuất dấu của số nguyên âm 0. Chúng tôi giải quyết điều đó bằng cách kiểm tra rõ ràng cho số không. Nếu chúng ta chơi golf thực sự, tất nhiên chúng ta có thể cho phép so sánh hai số không cũng thực hiện phép trừ.


1

C 80 ký tự

a,b,c=1<<31;main(){scanf("%d%d",&a,&b);putchar(a^b?(a&c^b&c?a:a-b)&c?60:62:61);}

Nó in '<', '>' hoặc '=', như vậy.

C 63 ký tự

Một cách tiếp cận mới:

a;main(b){scanf("%d%d",&a,&b);putchar(50+(0L+a-b>>42)+!(a-b));}

In '1', '2' hoặc '3'.


1

Trong 64 ký tự không có stdio.h

a,b;main(){scanf("%d%d",&a,&b);puts((a-b)>>31?"<":a^b?">":"=");}

in '>' nếu a> b, '<' nếu a <b, '=' nếu a == b int tràn là UB. Đừng tràn vào.

// declare a and b as ints
a,b;

// defaults to int
main()
{
  scanf("%d%d",&a,&b);
   /*
    * (a-b)>>31 signbit of a-b
    * a^b a xor b -> 0 if they are equal
    */
  puts(((a-b)>>31) ? "<" : (a^b) ? ">" : "=");
}
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.