การออกแบบฟังก์ชั่น f (f (n)) == -n


841

คำถามที่ฉันได้รับจากการสัมภาษณ์ครั้งล่าสุด:

ออกแบบฟังก์ชั่นfเช่น:

f(f(n)) == -n

ในกรณีที่nเป็น 32 บิตลงนามจำนวนเต็ม ; คุณไม่สามารถใช้เลขคณิตจำนวนเชิงซ้อนได้

หากคุณไม่สามารถออกแบบฟังก์ชั่นดังกล่าวสำหรับตัวเลขทั้งหมดได้ให้ออกแบบให้มีขนาดใหญ่ที่สุดเท่าที่จะเป็นไปได้

ความคิดใด ๆ


2
บทสัมภาษณ์นี้เป็นงานอะไร
tymtam

คำตอบ:


377

เกี่ยวกับ:

f (n) = sign (n) - (-1) n * n

ใน 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 จะส่งเสริมจำนวนเต็มโดยอัตโนมัติตามความยาวที่กำหนด ในภาษาอื่น ๆ จำนวนเต็มบวกที่ใหญ่ที่สุดจะล้นดังนั้นจึงจะทำงานได้สำหรับจำนวนเต็มทั้งหมดยกเว้นที่หนึ่ง


จะทำให้การทำงานสำหรับจำนวนจริงที่คุณต้องเปลี่ยนnใน (-1) n{ ceiling(n) if n>0; floor(n) if n<0 }กับ

ใน C # (ใช้ได้กับสองครั้งใด ๆ ยกเว้นในสถานการณ์ล้น):

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
ใช้งานไม่ได้สำหรับ -1 เนื่องจาก -1 * 0 ยังคงเป็น 0
Joel Coehoorn

3
ไม่มันไม่ใช่ f (-1) = 0. f (0) = 1
1800 ข้อมูล

5
มันเสียสำหรับ 1 อย่างไรก็ตาม f (1) = 0. f (0) = 1
1800 ข้อมูล

18
อืมการบันทึกสถานะด้วยเลขคี่และเลขคี่ฉันควรคิดอย่างนั้น
ไม่รู้จัก

38
ฉันคิดว่าสิ่งที่สำคัญที่สุดไม่ใช่ฟังก์ชั่นที่เกิดขึ้นจริง (มีโซลูชั่นมากมายเหลือหลาย) แต่เป็นกระบวนการที่คุณสามารถสร้างฟังก์ชั่นดังกล่าวได้
pyon

440

คุณไม่ได้พูดภาษาที่พวกเขาคาดหวัง ... นี่เป็นวิธีแก้ปัญหาแบบคงที่ (Haskell) โดยพื้นฐานแล้วมันยุ่งกับ 2 บิตที่สำคัญที่สุด:

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

มันง่ายกว่ามากในภาษาไดนามิก (Python) เพียงตรวจสอบว่าอาร์กิวเมนต์เป็นตัวเลข X และส่งกลับแลมบ์ดาที่ส่งกลับค่า -X:

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

23
เยี่ยมฉันชอบสิ่งนี้ ... วิธีการเดียวกันใน JavaScript: var f = function (n) {return (typeof n == 'function')? n (): function () {return -n; }}
ทำเครื่องหมาย Renouf

อาจเป็นได้ว่า Haskell ของฉันเป็นสนิมมาก แต่คุณได้ตรวจสอบว่า (f 0) หรือไม่ ดูเหมือนว่าควรจะให้ผลลัพธ์เช่นเดียวกับ (f 0x80000000) อย่างน้อยถ้าเราจัดการกับ int 32- บิตที่มีเลขคณิตแบบ wraparound (ในการดำเนินการปฏิเสธ) และนั่นจะไม่ดี
Darius Bacon

11
ผู้สัมภาษณ์โดยเฉลี่ยจะรู้ว่าแลมบ์ดาสร้างคืออะไร?
Jeremy Powell

4
แน่นอนว่าเคล็ดลับการโกงแบบนี้ยังทำงานใน Haskell แม้ว่าจะเป็นแบบคงที่: 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

4
อะไร? คุณได้รับความคิดที่ว่าประเภทของ f (n) === 'ฟังก์ชั่น' โดยเฉพาะอย่างยิ่งที่ n คือตัวเลขและคุณคาดหวังว่าตัวเลขจะถูกส่งกลับ? ฉันไม่เข้าใจว่ากรณีอินสแตนซ์สามารถนำไปใช้ที่นี่ได้อย่างไร ฉันพูด Python ไม่ดี แต่ในการตรวจสอบ JS ของอาร์กิวเมนต์สำหรับประเภทฟังก์ชั่นนั้นผิดปกติในกรณีนี้ วิธีแก้ปัญหาตัวเลขใช้ที่นี่เท่านั้น f คือฟังก์ชัน f (n) คือตัวเลข
แฮร์รี่

284

ต่อไปนี้เป็นข้อพิสูจน์ว่าทำไมฟังก์ชั่นดังกล่าวจึงไม่มีอยู่สำหรับตัวเลขทั้งหมดหากไม่ได้ใช้ข้อมูลเพิ่มเติม (ยกเว้น 32 บิตของ int):

เราจะต้องมี f (0) = 0 (หลักฐาน: สมมติว่า f (0) = x จากนั้น f (x) = f (f (0)) = -0 = 0 ทีนี้, -x = f (f (x ( )) = f (0) = x ซึ่งหมายความว่า x = 0)

นอกจากนี้สำหรับการใด ๆxและสมมติว่าy f(x) = yเราต้องการf(y) = -xแล้ว และf(f(y)) = -y => f(-x) = -y. เพื่อสรุป: ถ้าf(x) = yแล้วf(-x) = -yและและf(y) = -xf(-y) = x

ดังนั้นเราต้องหารจำนวนเต็มทั้งหมดยกเว้น 0 เป็นชุด 4 แต่เรามีเลขจำนวนเต็มเช่นนั้น ไม่เพียงแค่นั้นหากเราลบจำนวนเต็มที่ไม่มีค่าบวกเรายังมีหมายเลข 2 (mod4)

หากเราลบจำนวนสูงสุด 2 หมายเลขที่เหลือ (โดยค่า abs) เราจะได้ฟังก์ชัน:

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

แน่นอนตัวเลือกอื่นคือไม่ปฏิบัติตาม 0 และรับ 2 หมายเลขที่เราลบออกเป็นโบนัส (แต่นั่นเป็นเพียงโง่ถ้า)


29
ฉันไม่อยากจะเชื่อเลยว่าฉันต้องอ่านเรื่องนี้ไกลเพื่อหาวิธีแก้ปัญหาที่ดีที่จัดการกับจำนวนลบโดยไม่ต้องหันไปใช้ตัวแปรหรือกลวิธีระดับโลกที่ทำให้โค้ดยุ่งเหยิง ถ้าฉันสามารถลงคะแนนคุณมากกว่าหนึ่งครั้งฉันจะ
Kyle Simek

สังเกตดีว่ามีเลขคี่ของจำนวนเต็มภัณฑ์ในnลงนามบิต
Andres Jaan Tack

นี่จะเป็นคำตอบของฉันเช่นกัน แต่ระวังกรณีขอบn = -2147483648(ค่าต่ำสุด); คุณไม่สามารถทำได้abs(n)ในกรณีนั้นและผลลัพธ์จะไม่ถูกกำหนด (หรือข้อยกเว้น)
Kirk Broadhurst

1
@ a1kmm: ขออภัย-2³²ด้านบนควรเป็น-2³¹ อย่างไรก็ตามกรณีที่ f (0) ≠ 0 (และ f (0) = - 2³¹) เป็นกรณีที่ง่ายกว่าเนื่องจากเราแสดงว่าทั้งสองถูกตัดการเชื่อมต่อจากส่วนที่เหลือ อีกกรณีที่เราต้องพิจารณาคือ f (0) = 0 แต่ f (x) = - 2³¹สำหรับบาง x ≠ 0, x ≠-2³¹ ในกรณีนั้น f (-2³¹) = f (f (x)) = - x (note -x ไม่สามารถเป็น-2³¹ได้เนื่องจากไม่มี x ดังกล่าวอยู่) ปล่อยให้ f (-x) = y จากนั้น f (y) = f (f (-x)) = x อีกครั้ง y ไม่สามารถ-2³¹ (เหมือน f (y) = x แต่ f (-2³¹) = - x, และ x ไม่ใช่ 0) ดังนั้น-2³¹ = f (x) = f (f (y)) = - y ซึ่งเป็นไปไม่ได้ ดังนั้น 0 และ-2³¹จะต้องตัดการเชื่อมต่อจากส่วนที่เหลือ (ไม่ใช่ภาพของสิ่งอื่น)
ShreevatsaR

1
@ จะไม่มีเลขศูนย์ลงนามถ้า (ตามที่ฉันคิด) เรากำลังพูดถึงจำนวนเต็ม 32 บิตสองส่วน
goffrie

146

ขอบคุณการโอเวอร์โหลดใน 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
น่าเสียดายเนื่องจากชื่อ mangling ฟังก์ชันที่คุณเรียกว่า "f" มีชื่อแปลก ๆ
pyon

1
ฉันเคยคิดว่าจะเป็นอย่างนั้น แต่เมื่อคิดใน C นี่มันถูกโยนทิ้งไป ... ทำได้ดีมาก!
Liran Orevi

@Rui Craverio: มันจะไม่ทำงานใน. NET 3.5+ เพราะผู้เขียนเลือกที่จะใช้คำหลัก var เป็นชื่อตัวแปร
Kredns

72
ในทางเทคนิค ... นี่ไม่ใช่สิ่งที่คำถามต้องการ คุณกำหนดไว้ 2 f () ฟังก์ชั่น, f (int) และ f (float) และคำถามที่ถาม "การออกแบบฟังก์ชัน f () ..."
elcuco

2
@elcuco แน่นอนว่าทางเทคนิค แต่มีเหตุผลว่ามันเป็นฟังก์ชั่นเดียวที่มีโอเวอร์โหลดหลายตัว (คุณสามารถทำ f (f (42)) กับมันได้) เนื่องจากคำจำกัดความไม่ได้พูดอะไรเกี่ยวกับพารามิเตอร์และค่าส่งคืนฉันแทบจะไม่สามารถยอมรับได้ว่าเป็นคำจำกัดความทางเทคนิค
Marek Toman

135

หรือคุณอาจละเมิดผู้ประมวลผลล่วงหน้า:

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

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

ถ้าอย่างนั้นคุณจะได้คอนราด "เลอชิฟ" รูดอล์ฟแล้วเหรอ? ฉันจะเอาเสื้อ ใช่ฉันรู้เกี่ยวกับสิ่งที่เป็นโมฆะหลักทั้งหมด แต่เพิ่ม "return 0;" เป็นความพยายามพิเศษเพียงมาก ;-)
Skizz

25
@Skizz กลับ 0 จากหลักไม่จำเป็นต้องใช้ใน c ++ แม้จะมีค่าตอบแทน int ... ดังนั้นโดยการทำมันถูกต้องคุณจริง ๆ พิมพ์ตัวละครน้อย
Dan Olson

10
Skizz ละเมิดผู้ประมวลผลล่วงหน้าเสมอ: D
Arnis Lapsa

23
นี่ไม่ใช่ฟังก์ชั่น .. ดังนั้นนี่ไม่ใช่วิธีแก้ปัญหาที่ถูกต้อง
smerlin

3
@smerlin: มันเป็นฟังก์ชั่นอินไลน์ที่ส่งคืนฟังก์ชั่นอินไลน์: เนื้อความของทั้งสองจะขยายตัวในเวลารวบรวมหรือมากกว่าก่อน ไม่สามารถมีประสิทธิภาพมากไปกว่านี้อีกแล้ว
Jon Purdy

103

สิ่งนี้เป็นจริงสำหรับจำนวนลบทั้งหมด

    f (n) = abs (n)

เนื่องจากมีจำนวนหนึ่งที่เป็นลบมากขึ้นกว่าที่มีตัวเลขในเชิงบวกสำหรับเจ้าตัวจำนวนเต็มสมบูรณ์, f(n) = abs(n)ที่ถูกต้องสำหรับอีกหนึ่งกรณีกว่าการแก้ปัญหาที่เป็นเหมือนกันเช่นเดียวกับf(n) = n > 0 ? -n : n f(n) = -abs(n)มีคุณหนึ่ง ... : D

UPDATE

ไม่มันไม่ถูกต้องสำหรับอีกหนึ่งกรณีเนื่องจากฉันเพิ่งได้รับการยอมรับโดยความคิดเห็นของ litb ... abs(Int.Min)จะล้น ...

ฉันคิดเกี่ยวกับการใช้ข้อมูล mod 2 ด้วย แต่ได้ข้อสรุปว่ามันไม่ทำงาน ... ไปก่อน ถ้าทำถูกต้องมันจะใช้ได้กับทุกหมายเลขยกเว้นInt.Minเพราะจะล้น

UPDATE

ฉันเล่นกับมันซักพักหนึ่งแล้วมองหาเคล็ดลับการจัดการที่ดี แต่ฉันไม่สามารถหาซับในที่ดีได้ในขณะที่โซลูชัน mod 2 นั้นเข้ากันได้ดี

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

ใน C # สิ่งนี้จะกลายเป็นสิ่งต่อไปนี้:

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

เพื่อให้มันทำงานได้กับค่าทั้งหมดคุณต้องแทนที่Math.Abs()ด้วย(n > 0) ? +n : -nและรวมการคำนวณในuncheckedบล็อก จากนั้นคุณจะได้Int.Minแมปแม้กระทั่งกับตัวเองเป็น negation ไม่ จำกัด

UPDATE

แรงบันดาลใจจากคำตอบอื่นฉันจะอธิบายวิธีการทำงานของฟังก์ชั่นและวิธีการสร้างฟังก์ชั่นดังกล่าว

ให้เริ่มที่จุดเริ่มต้นมาก ฟังก์ชั่นfจะใช้ซ้ำกับค่าที่กำหนดnซึ่งให้ลำดับของค่า

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

คำถามที่ต้องการf(f(n)) = -nนั่นคือสองแอพพลิเคชั่นที่ต่อเนื่องของการfคัดค้านการโต้แย้ง แอปพลิเคชันเพิ่มเติมสองรายการf- สี่รายการ - ปฏิเสธการโต้แย้งอีกครั้งให้ผลnอีกครั้ง

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

ขณะนี้มีวัฏจักรที่ชัดเจนของความยาวสี่วง การแทนx = f(n)และการสังเกตว่าสมการที่ได้รับf(f(f(n))) = f(f(x)) = -xนั้นให้ผลดังนี้

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

ดังนั้นเราจึงได้วงจรความยาวสี่จำนวนสองตัว ถ้าคุณจินตนาการว่าวัฏจักรเป็นรูปสี่เหลี่ยมผืนผ้าค่าที่ถูกลบจะอยู่ที่มุมตรงข้าม

หนึ่งในหลาย ๆ วิธีในการสร้างวงจรดังกล่าวคือการเริ่มต้นจาก n

 n => คัดค้านและลบหนึ่งรายการ
-n - 1 = - (n + 1) => เพิ่มหนึ่งรายการ
-n => ปฏิเสธและเพิ่มหนึ่งรายการ
 n + 1 => ลบออกหนึ่งอัน
 n

+1 => -2 => -1 => +2 => +1ตัวอย่างที่เป็นรูปธรรมเป็นเช่นวงจรคือ เราเกือบเสร็จแล้ว เมื่อสังเกตว่าวงจรที่สร้างขึ้นนั้นมีจำนวนบวกแปลก ๆ แม้จะเป็นตัวตายตัวแทนและทั้งคู่ก็คัดค้านเราสามารถแบ่งจำนวนเต็มออกเป็นหลาย ๆ รอบได้อย่างง่ายดาย ( 2^32เป็นผลคูณของสี่) และได้พบฟังก์ชันที่ตรงตามเงื่อนไข

แต่เรามีปัญหากับศูนย์ รอบจะต้องมี0 => x => 0เนื่องจากศูนย์เป็นโมฆะกับตัวเอง และเนื่องจากวงจรกล่าวแล้วเป็นไปตาม0 => x 0 => x => 0 => xนี้เป็นเพียงวงจรของความยาวสองและจะกลายเป็นตัวเองหลังจากที่สองการใช้งานไม่เข้าx -xโชคดีที่มีกรณีหนึ่งที่สามารถแก้ไขปัญหาได้ ถ้าเท่ากับศูนย์เราได้รับรอบความยาวหนึ่งที่มีเพียงศูนย์และเราแก้ปัญหาที่สรุปว่าศูนย์เป็นจุดคงที่ของXf

ทำ? เกือบจะ เรามี2^32ตัวเลขศูนย์คือจุดคงที่ที่จะทิ้ง2^32 - 1ตัวเลขไว้และเราต้องแบ่งหมายเลขนั้นเป็นรอบของตัวเลขสี่ตัว ไม่ดีที่2^32 - 1ไม่ใช่ผลคูณของสี่ - จะมีตัวเลขสามตัวที่ไม่อยู่ในรอบความยาวสี่ใด ๆ

ผมจะอธิบายในส่วนที่เหลือของการแก้ปัญหาโดยใช้ชุดขนาดเล็ก 3 itegers บิตลงนามตั้งแต่การ-4 +3เราทำด้วยศูนย์ +1 => -2 => -1 => +2 => +1เรามีหนึ่งครบวงจร +3ตอนนี้ให้เราสร้างวงจรเริ่มต้นที่

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

ปัญหาที่เกิดขึ้น+4คือไม่สามารถแสดงได้ว่าเป็นจำนวนเต็ม 3 บิต เราจะได้รับ+4โดยกวน-3ไป+3- สิ่งที่ยังคงเป็นจำนวนเต็ม 3 บิตที่ถูกต้อง - แต่แล้วเพิ่มหนึ่งไป+3(ไบนารี011) อัตราผลตอบแทน100ไบนารี ตีความว่าเป็นจำนวนเต็มไม่ได้ลงนามมันเป็นแต่เราต้องตีความมันลงนามจำนวนเต็ม+4 -4ดังนั้น-4สำหรับตัวอย่างนี้หรือInt.MinValueในกรณีทั่วไปคือจุดคงที่ที่สองของการปฏิเสธเลขจำนวนเต็ม - 0 และInt.MinValueถูกแมปกับตัวเอง ดังนั้นวงจรจึงเป็นดังนี้

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

มันเป็นวงจรของความยาวสองและนอกจากนี้จะเข้าสู่วงจรผ่านทาง+3 -4ผลที่ตามมา-4คือการแมปอย่างถูกต้องกับตัวเองหลังจากที่สองฟังก์ชั่นการใช้งาน+3จะถูกแมปอย่างถูกต้อง-3หลังจากที่สองฟังก์ชั่นการใช้งาน แต่-3ถูกแมปผิดพลาดกับตัวเองหลังจากสองฟังก์ชั่น

ดังนั้นเราจึงสร้างฟังก์ชันที่ใช้งานได้กับจำนวนเต็มทั้งหมด แต่มีหนึ่งฟังก์ชัน เราทำได้ดีกว่านี้ไหม ไม่เราทำไมได้. ทำไม? เราต้องสร้างวงจรความยาวสี่และสามารถครอบคลุมช่วงจำนวนเต็มทั้งหมดถึงสี่ค่า ค่าที่เหลือคือจุดคงที่สองจุด0และInt.MinValueจะต้องแมปเข้ากับตัวเองและจำนวนเต็มตามอำเภอใจสองตัวxและ-xจะต้องแมปเข้าด้วยกันโดยแอปพลิเคชันฟังก์ชันสองตัว

ในการทำแผนที่xกับ-xและในทางกลับกันพวกเขาจะต้องตั้งสี่รอบและจะต้องอยู่ที่มุมตรงข้ามของรอบนั้น ผลที่ตามมา0และInt.MinValueจะต้องอยู่ที่มุมตรงข้ามเช่นกัน สิ่งนี้จะทำการแมปอย่างถูกต้องxและ-xสลับจุดคงที่ทั้งสอง0และInt.MinValueหลังจากแอปพลิเคชั่นสองฟังก์ชั่นและปล่อยให้เรามีอินพุตที่ล้มเหลวสองตัว ดังนั้นจึงเป็นไปไม่ได้ที่จะสร้างฟังก์ชั่นที่ใช้งานได้กับค่าทั้งหมด แต่เรามีหนึ่งที่ทำงานได้สำหรับทุกค่ายกเว้นหนึ่งและนี่คือสิ่งที่ดีที่สุดที่เราสามารถทำได้


ไม่ตรงตามเกณฑ์: abs (abs (n))! = -n
Dan Olson

แน่นอนว่าสำหรับตัวเลขลบทั้งหมดอย่างที่เขาพูด นั่นเป็นส่วนหนึ่งของคำถาม: หากคุณไม่สามารถหาข้อทั่วไปได้ให้หาข้อที่เหมาะกับช่วงกว้างที่สุดเท่าที่จะเป็นไปได้
jalf

คำตอบนี้อย่างน้อยก็ดีเหมือนคำตอบของ Marj Synowiec และ Rowland Shaw มันใช้งานได้กับตัวเลขที่แตกต่างกัน
1800 ข้อมูล

19
เพื่อนคุณอาจกำจัด "UPDATE" และเขียนคำตอบที่ถูกต้องหนึ่งข้อ 3/4 ด้านล่าง ("ได้แรงบันดาลใจจากคำตอบอื่น") ยอดเยี่ยมมาก
Andres Jaan Tack

1
ฉันชอบ abs-solution สำหรับตัวเลขลบ เรียบง่ายและเข้าใจง่าย
Thorbjørn Ravn Andersen

97

การใช้ตัวเลขที่ซับซ้อนคุณสามารถแบ่งงานการลบตัวเลขได้อย่างมีประสิทธิภาพเป็นสองขั้นตอน:

  • คูณ n ด้วย i แล้วคุณจะได้ n * i, ซึ่งหมุนได้ 90 องศาทวนเข็มนาฬิกา
  • คูณด้วย i อีกครั้งแล้วคุณจะได้ -n

สิ่งที่ดีคือคุณไม่จำเป็นต้องใช้รหัสการจัดการพิเศษ เพียงแค่คูณด้วยฉันก็ทำงาน

แต่คุณไม่ได้รับอนุญาตให้ใช้หมายเลขที่ซับซ้อน ดังนั้นคุณต้องสร้างแกนจินตภาพของคุณเองโดยใช้ส่วนของช่วงข้อมูลของคุณ เนื่องจากคุณต้องการค่าจินตภาพ (ระดับกลาง) เท่ากับค่าเริ่มต้นคุณจึงเหลือช่วงข้อมูลเพียงครึ่งเดียว

ฉันพยายามที่จะเห็นภาพนี้ในรูปต่อไปนี้โดยสมมติว่าข้อมูล 8 บิตที่ลงนามแล้ว คุณจะต้องปรับขนาดนี้สำหรับจำนวนเต็ม 32 บิต ช่วงที่อนุญาตสำหรับ n เริ่มต้นคือ -64 ถึง +63 นี่คือสิ่งที่ฟังก์ชันทำเพื่อผลบวก n:

  • ถ้า n อยู่ใน 0..63 (ช่วงเริ่มต้น) การเรียกใช้ฟังก์ชันจะเพิ่ม 64, การจับคู่ n ไปยังช่วง 64 .. 127 (ช่วงกลาง)
  • ถ้า n อยู่ใน 64..127 (ช่วงกลาง) ฟังก์ชันจะลบ n จาก 64 โดยจับคู่กับช่วง 0 ถึง 63

สำหรับค่าลบ n ฟังก์ชันจะใช้ช่วงกลาง -65 ..- 128

ข้อความแสดงแทน


4
@geschema คุณใช้เครื่องมือใดในการสร้างกราฟิกที่ดีเหล่านั้น
jwfearn

10
ขออภัยคำถามบอกว่าไม่มีตัวเลขที่ซับซ้อนอย่างชัดเจน
Rui Craveiro

6
@Liran: ฉันใช้ OmniGraffle (สำหรับ Mac เท่านั้น)
geschema

39
+1 ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุด ฉันไม่คิดว่าคนอ่านพอเพราะพวกเขาทุกคนสังเกตว่าคำถามที่กล่าวว่าไม่สามารถใช้ตัวเลขที่ซับซ้อน ฉันอ่านทุกสิ่งและคุณอธิบายวิธีแก้ปัญหาในจำนวนเชิงซ้อนเพื่อกำหนดระยะสำหรับการแก้ปัญหาที่ไม่ซับซ้อนตามคำถามที่ถาม ทำได้ดีมาก
jrista

1
@jrista โซลูชันทั้งหมดใช้มิติที่สองซึ่งเป็นทั้งหมดที่ 'จำนวนเชิงซ้อน' จริงๆ (ส่วนใหญ่ใช้คี่เทียบกับคู่และข้างบนใช้floatvs int) '4 องค์ประกอบแหวน' ที่คำตอบมากมายอธิบายถึงความจำเป็น 4 สถานะซึ่งสามารถแสดงเป็น 2 มิติแต่ละอันมี 2 สถานะ ปัญหากับคำตอบนี้ก็คือว่ามันต้องใช้พื้นที่ในการประมวลผลเพิ่มเติม (เท่านั้น 'งาน' สำหรับ -64..63 แต่ความต้องการ -128..127 พื้นที่) และไม่ได้ระบุอย่างชัดเจนสูตรเขียน!
Kirk Broadhurst

65

ทำงานได้ยกเว้น int.MaxValue และ 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));
    }

เป็นภาพ


ไม่แน่ใจว่าทำไมสิ่งนี้จึงถูกลดระดับลง มันอินพุตอะไรล้มเหลว?
Rodrick Chapman

ทำไมคุณไม่ใช้ฟังก์ชัน signum?!?
comonad

1
ภาพดีจริงๆ ส่ง0ไป0และ-2147483648เพื่อจุดตั้งแต่เหล่านี้ตัวเลขสองได้รับการแก้ไขสำหรับผู้ประกอบการปฏิเสธการ-2147483648 x => -xสำหรับตัวเลขที่เหลือให้ทำตามลูกศรในภาพด้านบน ชัดเจนจากคำตอบและความคิดเห็นของ SurDin จะมีตัวเลขสองตัวในกรณีนี้2147483647และ-2147483647ไม่มีคู่อื่นเหลือเพื่อแลกเปลี่ยน
Jeppe Stig Nielsen

ดูเหมือนว่าจะเป็นรอยยิ้ม - มีริ้วรอยมากมาย
Anshul

48

คำถามที่ไม่ได้พูดอะไรเกี่ยวกับสิ่งที่ประเภทของการป้อนข้อมูลและค่าตอบแทนของฟังก์ชั่นfจะต้องมี (อย่างน้อยไม่ได้เป็นวิธีที่คุณนำเสนอ) ...

... แค่นั้นเมื่อ n เป็นจำนวนเต็ม 32 บิตแล้ว f(f(n)) = -n

แล้วมีอะไรที่ชอบ

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

ถ้า n เป็นจำนวนเต็ม 32 บิตคำสั่งf(f(n)) == -nจะเป็นจริง

เห็นได้ชัดว่าวิธีนี้สามารถขยายการทำงานสำหรับตัวเลขที่กว้างยิ่งขึ้น ...


2
ส่อเสียด. จำกัด จำนวนอักขระ
Joe Phillips

2
ใช่ฉันกำลังทำงานในแนวทางที่คล้ายกัน คุณชนะฉันมัน +1 :)
jalf

1
ฉลาดมาก! สิ่งนี้อยู่ใกล้กับ (& อย่างมีประสิทธิภาพเหมือนกับ) โดยใช้ตัวเลขที่ซับซ้อนซึ่งจะเป็นทางออกที่ชัดเจน & อุดมคติ แต่ไม่ได้รับอนุญาตอย่างชัดเจน ทำงานนอกช่วงของตัวเลขที่อนุญาต
Kirk Broadhurst

48

สำหรับ javascript (หรือภาษาที่พิมพ์แบบไดนามิกอื่น ๆ ) คุณสามารถให้ฟังก์ชั่นนี้ยอมรับทั้ง int หรือวัตถุและคืนค่าอื่น ๆ กล่าวคือ

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

ให้

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

อีกทางหนึ่งคุณสามารถใช้งานมากไปในภาษาที่พิมพ์ออกมาอย่างรุนแรงแม้ว่ามันอาจจะผิดกฎเช่นกัน

int f(long n) {
    return n;
}

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

หลังไม่ได้หมายถึงความต้องการของฟังก์ชั่น "a" (เอกพจน์) :)
Drew

ลบคำตอบครึ่งหลังและนี่คือคำตอบที่ถูกต้อง
jmucchiello

@Drew ซึ่งเป็นเหตุผลที่ผมกล่าวว่ามันอาจจะทำลายกฎ
cobbal

2
ใน JavaScript ฟังก์ชั่นเป็นวัตถุและสามารถรักษาสถานะไว้ได้
Nosredna

1
IMO: ฟังก์ชั่น f (n) {return n.passed? -n.val: {val: n, ส่งผ่าน: 1}} สามารถอ่านได้และสั้นกว่า
SamGoody

46

ขึ้นอยู่กับแพลตฟอร์มของคุณบางภาษาอนุญาตให้คุณรักษาสถานะในฟังก์ชั่น VB.Net ตัวอย่างเช่น:

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

    Return n * flag
End Function

IIRC, C ++ อนุญาตสิ่งนี้เช่นกัน ฉันสงสัยว่าพวกเขากำลังมองหาทางออกที่แตกต่างกัน

แนวคิดอื่นคือเนื่องจากพวกเขาไม่ได้กำหนดผลลัพธ์ของการเรียกครั้งแรกไปยังฟังก์ชันคุณสามารถใช้เลขคี่ / คู่เพื่อควบคุมว่าจะกลับเครื่องหมาย:

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

เพิ่มหนึ่งค่าตามขนาดของตัวเลขคู่ทั้งหมดลบออกจากขนาดของตัวเลขคี่ทั้งหมด ผลลัพธ์ของการโทรสองครั้งมีขนาดเท่ากัน แต่การโทรหนึ่งครั้งที่มันอยู่แม้เราจะสลับเครื่องหมาย มีบางกรณีที่สิ่งนี้จะไม่ทำงาน (-1, max หรือ min int) แต่มันใช้งานได้ดีกว่าสิ่งอื่นใดที่แนะนำมาจนถึงตอนนี้


1
ฉันเชื่อว่าใช้งานได้กับ MAX_INT เพราะมันแปลกเสมอ มันไม่ทำงานสำหรับ MIN_INT และ -1
Airsource Ltd

9
มันไม่ใช่ฟังก์ชั่นถ้ามันมีผลข้างเคียง
nos

12
นั่นอาจเป็นจริงในทางคณิตศาสตร์ แต่มันไม่เกี่ยวข้องกับการเขียนโปรแกรม ดังนั้นคำถามคือว่าพวกเขากำลังมองหาวิธีการแก้ปัญหาทางคณิตศาสตร์หรือวิธีการเขียนโปรแกรม แต่ได้รับว่ามันเป็นงานเขียนโปรแกรม ...
ไรอันลุน

+1 ฉันจะโพสต์หนึ่งใน C ด้วย "static int x" การใช้ FIFO ด้วยการปฏิเสธของผลลัพธ์ แต่นี่ใกล้พอ
phkahler

2
@ ไม่มี: ใช่มันเป็นเพียงไม่โปร่งใส referential
Clark Gaebel

26

ใช้ประโยชน์จากข้อยกเว้น JavaScript

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

f(f(0)) => 0

f(f(1)) => -1


ฉันสงสัยว่ามีการใช้ข้อยกเว้นเช่นนี้มาก่อน ... :)
NoBugs

+1 นอกกรอบความคิด เย็น! แต่ในรหัสการผลิตฉันจะใช้ typeof เพื่อความปลอดภัย

21

สำหรับค่า 32- บิตทั้งหมด (โดยมีค่าที่ -0 คือ -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);
}

คุณจำเป็นต้องจับคู่แต่ละ -x => x => -x loop กับ ay => -y => y loop splitดังนั้นผมจึงจับคู่ตรงข้ามด้านข้างของ

เช่นสำหรับจำนวนเต็ม 4 บิต:

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

21

เวอร์ชัน C ++ อาจดัดงอกฎบ้าง แต่ใช้ได้กับทุกประเภทตัวเลข (ลอย, ints, doubles) และแม้กระทั่งประเภทคลาสที่เกินค่าลบยูนารี:

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

ความคิดที่ดี. คุณอาจสูญเสียโครงสร้างและแทนที่จะให้ฟังก์ชันหนึ่งส่งกลับตัวชี้แทนฟังก์ชันอื่นที่อ้างถึงและปฏิเสธ
Imbue

20

x86 asm (สไตล์ 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

การตรวจสอบรหัส, จำนวนเต็ม 32 บิตที่เป็นไปได้ทั้งหมด, ข้อผิดพลาดกับ -2147483647


19

ใช้ globals ... แต่งั้นเหรอ?

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

3
ไม่แน่ใจว่านี่เป็นความตั้งใจของผู้ถามคำถาม แต่ +1 สำหรับ "คิดนอกกรอบ"
Liran Orevi

5
แทนที่จะพูดว่า "done = true" แบบมีเงื่อนไขคุณควรพูดว่า "done =! Done" เสมอเพื่อให้ฟังก์ชันของคุณสามารถใช้งานได้มากกว่าหนึ่งครั้ง
คริสลัทซ์

@Chris เนื่องจากการตั้งค่าที่ทำไว้เป็นจริงนั้นอยู่ในบล็อก if (! เสร็จสิ้น) จึงเท่ากับการทำ =! Done แต่! Done ไม่จำเป็นต้องคำนวณ (หรือปรับให้เหมาะสมโดยคอมไพเลอร์หากฉลาดพอ) .
nsayer

1
ความคิดแรกของฉันก็คือการแก้ปัญหานี้โดยใช้ตัวแปรทั่วโลกแม้ว่ามันจะรู้สึกเหมือนการโกงสำหรับคำถามนี้ อย่างไรก็ตามฉันขอยืนยันว่าวิธีการแก้ปัญหาตัวแปรทั่วโลกเป็นทางออกที่ดีที่สุดตามข้อกำหนดในคำถาม การใช้โลกทำให้ง่ายต่อการเข้าใจสิ่งที่เกิดขึ้น ฉันจะเห็นด้วยว่า a! เพียงแค่ย้ายสิ่งนั้นออกไปข้างนอกถ้าข้อ
Alderath

3
ในทางเทคนิคทุกอย่างที่รักษาสถานะไม่ได้เป็นฟังก์ชั่น แต่เป็นเครื่องของรัฐ ตามคำนิยามฟังก์ชั่นจะให้เอาต์พุตเดียวกันสำหรับอินพุตเดียวกันเสมอ
Ted Hopp

19

วิธีการแก้ปัญหา Perl นี้ทำงานสำหรับจำนวนเต็มลอยและสตริง

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

ลองทดสอบข้อมูล

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

เอาท์พุท:

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

แต่มันก็ไม่ได้ทำให้มัน int คุณกำลังจัดเก็บข้อมูลตัวแปรทั่วโลกในตัว "n" ตัวเอง ... ยกเว้นว่ามันไม่ใช่ int มิฉะนั้นคุณจะทำเช่นนั้นไม่ได้ ตัวอย่างเช่นถ้าnเป็นสตริงที่ฉันสามารถทำให้ 548 กลายเป็น "First_Time_548" แล้วในครั้งต่อไปที่มันทำงานผ่านฟังก์ชั่น ... ถ้า (prefix == First_Time_ ") แทน" First_Time_ "ด้วย" - "
Albert Renshaw

@AlbertRenshaw ไม่แน่ใจว่าคุณได้รับความคิดเหล่านั้นที่ไหน (1) ไม่มีตัวแปรระดับโลกที่เกี่ยวข้องที่นี่ (2) ถ้าคุณให้ฟังก์ชั่นเป็น int คุณจะได้รับข้อมูลกลับ - หรือการอ้างอิงถึง int หากคุณเรียกใช้ฟังก์ชันเป็นจำนวนคี่ (3) อาจจะมากที่สุดพื้นฐานนี้เป็น Perl สำหรับวัตถุประสงค์ในทางปฏิบัติทั้งหมด ints และสตริงสามารถใช้แทนกันได้อย่างสมบูรณ์ สตริงที่มีลักษณะเหมือนตัวเลขจะทำงานได้อย่างสมบูรณ์เช่นเดียวกับตัวเลขในบริบทส่วนใหญ่และตัวเลขจะมีความสุขเมื่อใดก็ตามที่ถาม
FMc

ขออภัยฉันไม่ทราบว่า perl ดูเหมือนว่าคุณกำลังใช้อาร์เรย์ระดับโลกฮ่าฮ่า
Albert Renshaw

18

ไม่มีใครเคยพูดว่า f (x) ต้องเป็นคนประเภทเดียวกัน

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


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

16

ฉันไม่ได้พยายามแก้ไขปัญหาเอง แต่มีความคิดเห็นสองสามอย่างเนื่องจากคำถามระบุว่าปัญหานี้ถูกวางเป็นส่วนหนึ่งของการสัมภาษณ์ (job?):

  • ฉันจะถามก่อนว่า "ทำไมต้องมีฟังก์ชั่นเช่นนี้ปัญหาที่ใหญ่กว่านี้เป็นส่วนหนึ่งของอะไร" แทนที่จะพยายามแก้ไขปัญหาที่เกิดขึ้นจริงในจุดที่ นี่แสดงให้เห็นว่าฉันคิดอย่างไรและจัดการกับปัญหาเช่นนี้ได้อย่างไร ใครรู้? นั่นอาจเป็นเหตุผลจริงที่คำถามถูกถามในการสัมภาษณ์ในตอนแรก หากคำตอบคือ "ไม่เป็นไรคิดว่ามันจำเป็นและแสดงให้ฉันเห็นว่าคุณจะออกแบบฟังก์ชั่นนี้อย่างไร ฉันจะทำเช่นนั้นต่อไป
  • จากนั้นฉันจะเขียนรหัสกรณีทดสอบ C # ที่ฉันจะใช้ (ชัดเจน: วนซ้ำจากint.MinValueไปยังint.MaxValueและสำหรับแต่ละnช่วงการโทรf(f(n))และตรวจสอบผลลัพธ์คือ-n) บอกว่าฉันจะใช้ Test Driven Development เพื่อรับฟังก์ชั่นดังกล่าว
  • เฉพาะในกรณีที่ผู้สัมภาษณ์ยังคงขอให้ฉันแก้ปัญหาที่เกิดขึ้นจริงฉันจะเริ่มต้นลอง pseudocode ปลอมในระหว่างการสัมภาษณ์ตัวเองเพื่อพยายามหาคำตอบบางอย่าง อย่างไรก็ตามฉันไม่คิดว่าฉันจะกระโดดเพื่อรับงานหากผู้สัมภาษณ์จะเป็นตัวบ่งชี้ว่า บริษัท เป็นอย่างไร ...

โอ้คำตอบนี้ถือว่าการสัมภาษณ์เป็นตำแหน่งที่เกี่ยวข้องกับการเขียนโปรแกรม C # แน่นอนว่าจะเป็นคำตอบที่ไร้สาระหากการสัมภาษณ์นั้นเกี่ยวข้องกับคณิตศาสตร์ ;-)


7
คุณจะโชคดีที่พวกเขาถามหา 32 int ถ้ามันเป็น 64 บิตการสัมภาษณ์จะไม่ดำเนินการต่อหลังจากที่คุณเรียกใช้การทดสอบ ;-)
alex2k8

แน่นอนถ้าฉันจะได้รับจุดที่จะเขียนการทดสอบนั้นจริงและเรียกใช้ในระหว่างการสัมภาษณ์ ;-) จุดของฉัน: ฉันจะพยายามไม่ให้ถึงจุดนั้นในการสัมภาษณ์เลย การเขียนโปรแกรมเป็น "วิธีคิด" มากกว่า "เขาเขียนบรรทัดของโค้ด" ในความคิดของฉันได้อย่างไร
peSHIr

7
อย่าทำตามคำแนะนำนี้ในการสัมภาษณ์จริง ผู้สัมภาษณ์คาดหวังให้คุณตอบคำถามจริง การตั้งคำถามถึงความเกี่ยวข้องของคำถามจะไม่ซื้ออะไรให้คุณ แต่มันอาจรบกวนผู้สัมภาษณ์ การออกแบบการทดสอบเล็ก ๆ น้อย ๆ นั้นไม่ได้นำคุณเข้าใกล้คำตอบมากขึ้นและคุณไม่สามารถทดสอบในการสัมภาษณ์ได้ หากคุณได้รับข้อมูลเพิ่มเติม (32 บิต) ให้ลองคิดดูว่ามันมีประโยชน์อย่างไร
Stefan Haustein

ผู้สัมภาษณ์ที่ได้รับความรำคาญเมื่อฉันขอข้อมูลเพิ่มเติม (ในขณะที่อาจถามถึงความเกี่ยวข้องของคำถามของเขาในกระบวนการ) ไม่ใช่ผู้สัมภาษณ์ที่ฉันต้องการทำงานด้วย / ดังนั้นฉันจะถามคำถามแบบนี้ต่อไปในการสัมภาษณ์ หากพวกเขาไม่ชอบฉันอาจจะยุติการสัมภาษณ์เพื่อหยุดการสูญเสียเวลาของเราอีกต่อไป ไม่ชอบความคิด "ฉันแค่ทำตามคำสั่ง" ตั้งหนึ่งบิต คุณ ..
peSHIr

16

ฉันขอเปลี่ยน 2 บิตที่สำคัญที่สุด

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

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

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

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

อย่างที่คุณเห็นมันเป็นแค่ส่วนเสริมเท่านั้น

ฉันจะได้คำตอบอย่างไร ความคิดแรกของฉันคือต้องการความสมมาตร 4 รอบเพื่อกลับไปที่ที่ฉันเริ่ม ตอนแรกฉันคิดว่านั่นคือรหัสสีเทา 2 บิต จากนั้นฉันคิดว่าจริง ๆ แล้วเลขฐานสองมาตรฐานก็เพียงพอแล้ว


ปัญหาของวิธีนี้คือมันไม่ทำงานกับตัวเลขลบสองคำชม (ซึ่งเป็นสิ่งที่ CPU สมัยใหม่ทุกเครื่องใช้) นั่นเป็นเหตุผลที่ฉันลบคำตอบที่เหมือนกันของฉัน
Tamas Czinege

คำถามที่ระบุจำนวนเต็ม 32 บิตที่ลงนามแล้ว โซลูชันนี้ใช้ไม่ได้กับการเติมเต็มสองส่วนหรือแทนส่วนเสริมของเลขจำนวนเต็ม 32 บิตที่เซ็นชื่อแล้ว มันจะใช้งานได้สำหรับการแสดงสัญลักษณ์ - และ - ขนาดซึ่งไม่ธรรมดามากในคอมพิวเตอร์สมัยใหม่ (นอกเหนือจากตัวเลขจุดลอยตัว)
Jeffrey L Whitledge

1
@DrJokepu - ว้าวหลังจากหกเดือน - นำโชคร้ายมา!
Jeffrey L Whitledge

คุณไม่เพียงแค่ต้องแปลงตัวเลขเป็นการแสดงสัญลักษณ์และขนาดภายในฟังก์ชันทำการแปลงจากนั้นแปลงกลับเป็นสิ่งที่การแทนค่าจำนวนเต็มดั้งเดิมก่อนที่จะส่งคืนหรือไม่
Bill Michell

ฉันชอบที่คุณนำมาใช้โดยทั่วไปตัวเลขที่ซับซ้อนโดยการแนะนำบิตจินตนาการ :)
jabirali

16

นี่คือวิธีแก้ไขปัญหาที่ได้รับแรงบันดาลใจจากข้อกำหนดหรืออ้างว่าไม่สามารถใช้ตัวเลขที่ซับซ้อนเพื่อแก้ไขปัญหานี้ได้

การคูณด้วยสแควร์รูทของ -1 เป็นแนวคิดที่ดูเหมือนว่าจะล้มเหลวเพราะ -1 ไม่มีรากที่สองเหนือจำนวนเต็ม แต่การเล่นโดยใช้โปรแกรมอย่าง mathematica ยกตัวอย่างเช่นสมการ

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

และนี่ก็เกือบจะดีเท่ากับการมีสแควร์รูทของ -1 ผลลัพธ์ของฟังก์ชันต้องเป็นจำนวนเต็มที่ลงนาม ดังนั้นฉันจะใช้ modulo operation mods (x, n) ที่คืนค่าจำนวนเต็ม y ให้สอดคล้องกับ x modulo n ที่ใกล้เคียงกับ 0 มากที่สุดการเขียนโปรแกรมภาษาเพียงเล็กน้อยเท่านั้นที่สามารถทำได้ แต่มันสามารถกำหนดได้อย่างง่ายดาย . เช่นในไพ ธ อนมันคือ:

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

การใช้สมการข้างต้นปัญหาสามารถแก้ไขได้ในขณะนี้

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

ตอบสนองนี้สำหรับจำนวนเต็มทั้งหมดในช่วงf(f(x)) = -x ผลลัพธ์ของยังอยู่ในช่วงนี้ แต่แน่นอนว่าการคำนวณจะต้องเป็นจำนวนเต็ม 64 บิต[-231-2, 231-2]f(x)


13

C # สำหรับช่วง 2 ^ 32 - 1 หมายเลขทุกหมายเลข int32 ยกเว้น (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

พิมพ์:

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

สิ่งนี้ยังใช้ไม่ได้กับ f (0) ซึ่งคือ 1073741824 f (1073741824) = 0. f (f (1073741824)) = 1073741824
Dinah

โดยทั่วไปคุณสามารถพิสูจน์ได้ว่าสำหรับบิตจำนวนเต็มสองชนิดของขนาดบิตใด ๆ ฟังก์ชันจะต้องไม่ทำงานสำหรับค่าอินพุตอย่างน้อยสองค่า
คนเกียจคร้าน

12

โดยพื้นฐานแล้วฟังก์ชั่นจะต้องแบ่งช่วงที่มีอยู่ออกเป็นรอบของขนาด 4 โดยมี -n ที่ปลายด้านตรงข้ามของรอบของ n อย่างไรก็ตาม 0 ต้องเป็นส่วนหนึ่งของวงจรที่มีขนาด 1 0->x->0->x != -xเพราะมิฉะนั้น เนื่องจาก 0 อยู่คนเดียวจะต้องมี 3 ค่าอื่น ๆ ในช่วงของเรา (ซึ่งมีขนาดเป็นทวีคูณของ 4) ไม่อยู่ในรอบที่เหมาะสมกับองค์ประกอบที่ 4

ผมเลือกค่าแปลก ๆ เหล่านี้เป็นพิเศษในการเป็นMIN_INT, และMAX_INT MIN_INT+1นอกจากนี้MIN_INT+1แผนที่จะMAX_INTถูกต้อง แต่ติดอยู่ที่นั่นและไม่ทำแผนที่ย้อนกลับ ฉันคิดว่านี่คือการประนีประนอมที่ดีที่สุดเพราะมันมีคุณสมบัติที่ดีของค่าที่รุนแรงเท่านั้นที่ทำงานไม่ถูกต้อง นอกจากนี้ยังหมายความว่ามันจะใช้งานได้กับBigInts ทั้งหมด

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

ไม่มีใครพูดว่ามันจะต้องไร้สัญชาติ

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

การโกง แต่ไม่มากเท่าตัวอย่าง สิ่งที่เลวร้ายยิ่งกว่านั้นก็คือการดูสแต็กเพื่อดูว่าที่อยู่ของผู้โทรของคุณเป็น & f แต่สิ่งนี้จะเป็นแบบพกพามากกว่า (แม้ว่าจะไม่ปลอดภัยสำหรับเธรด ... เวอร์ชันของเธรดที่ปลอดภัยจะใช้ TLS) ความชั่วร้ายมากยิ่งขึ้น:

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

แน่นอนว่าสิ่งเหล่านี้ไม่ได้ผลดีเกินไปสำหรับกรณีของ MIN_INT32 แต่มีค่าเล็กน้อยที่คุณสามารถทำได้เกี่ยวกับสิ่งนั้นเว้นแต่คุณจะได้รับอนุญาตให้กลับประเภทที่กว้างขึ้น


คุณสามารถ 'อัปเกรด' เพื่อถามเกี่ยวกับที่อยู่ (ใช่คุณต้องรับโดยอ้างอิง \ เป็นตัวชี้) - ใน C ตัวอย่างเช่น: int f (int & n) {static int * addr = & n; if (addr == & n) {return -n; } คืน n; }
IUnknownPointer

11

ฉันจินตนาการได้ว่าการใช้บิตที่ 31 เป็นบิตจินตภาพ ( i ) จะเป็นวิธีที่สนับสนุนครึ่งหนึ่งของช่วงทั้งหมด


นี่จะซับซ้อนกว่า แต่ไม่มีประสิทธิภาพมากกว่าคำตอบที่ดีที่สุดในปัจจุบัน
ข้อมูล 1800

1
@ 1800 ข้อมูล: ในทางกลับกันโดเมน [-2 ^ 30 + 1, 2 ^ 30-1] นั้นต่อเนื่องกันซึ่งน่าสนใจมากกว่าจากมุมมองทางคณิตศาสตร์
เช็นวอลเตอร์

10

ทำงานได้สำหรับ 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

ปัญหาฯ "32 บิตลงนามจำนวนเต็ม" แต่ไม่ได้ระบุว่าพวกเขาจะมีเจ้าตัว-สมบูรณ์หรือคน-สมบูรณ์

หากคุณใช้แบบเติมเต็มค่าทั้งหมด 2 ^ 32 จะเกิดขึ้นในรอบความยาวสี่ - คุณไม่จำเป็นต้องใช้ตัวพิมพ์พิเศษสำหรับศูนย์และคุณไม่จำเป็นต้องมีเงื่อนไข

ใน C:

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

งานนี้โดย

  1. การแลกเปลี่ยนบล็อกสูงและต่ำ 16 บิต
  2. อินเวอร์เตอร์หนึ่งช่วงตึก

หลังจากผ่านไปสองครั้งเราก็มีค่าผกผันของค่าเดิม ซึ่งในการเป็นตัวแทนของส่วนประกอบนั้นเทียบเท่ากับการปฏิเสธ

ตัวอย่าง:

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
แล้วไบต์ในสถาปัตยกรรมที่แตกต่างกันล่ะ?
สตีเวน

1
เลขคณิตทั้งหมดคือ 32 บิต ฉันไม่ได้จัดการแต่ละไบต์ดังนั้นลำดับไบต์จะไม่ส่งผลกระทบต่อมัน
finnw

ฟังดูใกล้เคียง คุณสามารถสมมติว่าการป้อนข้อมูลเป็น 2 ส่วน ดังนั้นคุณแปลงเป็นสัญลักษณ์บิตแทน ตอนนี้ขึ้นอยู่กับบิตสุดท้ายคุณพลิกบิตแรกและบิตสุดท้ายหรือเพียงแค่บิตสุดท้าย โดยทั่วไปคุณจะลบล้างเลขคู่และวนซ้ำ / คี่ตลอดเวลา ดังนั้นคุณจะได้รับกลับจากคี่ไปเป็นคี่และแม้กระทั่งหลังจาก 2 สาย ในตอนท้ายคุณแปลงกลับเป็น 2 ส่วนประกอบ ได้โพสต์รหัสสำหรับที่นี่ด้านล่าง
สเตฟาน Haustein

9

: D

boolean inner = true;

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

5
อาจทำให้คุณอภิปรายว่าทำไมตัวแปรทั่วโลกถึงไม่ดีถ้าพวกเขาไม่เตะคุณออกจากการสัมภาษณ์ที่นั่น!
palswim


7

ฉันต้องการแบ่งปันมุมมองของฉันเกี่ยวกับปัญหาที่น่าสนใจนี้ในฐานะนักคณิตศาสตร์ ฉันคิดว่าฉันมีทางออกที่มีประสิทธิภาพที่สุด

ถ้าฉันจำได้อย่างถูกต้องคุณจะลบล้างจำนวนเต็ม 32 บิตที่เซ็นชื่อด้วยการเพียงแค่โยนบิตแรก ตัวอย่างเช่นถ้า n = 1001 1101 1110 1011 1110 0000 1110 1010 ดังนั้น -n = 0001 1101 1110 1011 1110 0000 1110 1010

แล้วเราจะกำหนดฟังก์ชั่น f ที่ใช้จำนวนเต็ม 32- บิตที่ถูกเซ็นชื่อและส่งกลับจำนวนเต็ม 32- บิตที่เซ็นชื่ออีกครั้งพร้อมคุณสมบัติที่รับ f สองครั้งเหมือนกับการพลิกบิตแรก

ผมขอใช้คำถามใหม่โดยไม่พูดถึงแนวคิดทางคณิตศาสตร์เช่นจำนวนเต็ม

เราจะกำหนดฟังก์ชั่น f ที่ใช้ลำดับของศูนย์และความยาว 32 และส่งกลับลำดับของศูนย์และความยาวเดียวกันโดยมีคุณสมบัติที่รับ f สองครั้งเหมือนกับการพลิกบิตแรก

การสังเกต: ถ้าคุณสามารถตอบคำถามข้างต้นสำหรับกรณี 32 บิตคุณสามารถตอบคำถาม 64 บิตกรณี 100 บิต ฯลฯ คุณเพียงแค่ใช้ f กับ 32 บิตแรก

ตอนนี้ถ้าคุณสามารถตอบคำถามสำหรับกรณี 2 บิต Voila!

และใช่ปรากฎว่าการเปลี่ยน 2 บิตแรกนั้นเพียงพอแล้ว

นี่คือรหัสเทียม

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.

หมายเหตุ: ขั้นตอนที่ 2 และขั้นตอนที่ 3 เข้าด้วยกันสามารถ Summerised เป็น (a, b) -> (-b, a) ดูคุ้น ๆ เหรอ? นั่นควรเตือนคุณถึงการหมุน 90 องศาของระนาบและการคูณด้วยสแควร์รูทของ -1

ถ้าฉันแค่นำเสนอโค้ดหลอกเพียงอย่างเดียวโดยไม่มีการโหมโรงมานานดูเหมือนว่ากระต่ายจะออกจากหมวกฉันต้องการอธิบายว่าฉันได้วิธีแก้ปัญหาอย่างไร


6
ใช่มันเป็นปัญหาที่น่าสนใจ คุณรู้คณิตศาสตร์ของคุณ แต่นี่เป็นปัญหาด้านวิทยาศาสตร์คอมพิวเตอร์ ดังนั้นคุณต้องศึกษาคอมพิวเตอร์ การแสดงสัญลักษณ์ขนาดนั้นทำได้ แต่ทำได้เกิน 60 ปีมาแล้ว 2's-complement เป็นที่นิยมมากที่สุด
โปรแกรมเมอร์ Windows

5
นี่คือหน้าที่ของคุณกับสองบิตเมื่อใช้สองครั้ง: (a, b) -> (-b, a) -> (-a, -b) แต่เราพยายามไปถึง (-a, b) ไม่ใช่ (-a, -b)
buti-oxa

@ buti-oxa คุณพูดถูก การดำเนินการสองบิตควรเป็นเช่น: 00 -> 01 -> 10 -> 11 -> 00 แต่จากนั้นอัลกอริทึมของฉันถือว่าการแสดงขนาดสัญญาณซึ่งไม่เป็นที่นิยมในขณะนี้ตามที่โปรแกรมเมอร์ของ Windows กล่าวดังนั้นฉันคิดว่าอัลกอริทึม .
Yoo

ดังนั้นเขาไม่สามารถทำตามขั้นตอนสองครั้งแทนที่จะเป็นครั้งเดียวได้หรือ
Nosredna

4
buti-oxa ถูกต้องอย่างสมบูรณ์: ฟังก์ชั่นไม่ได้พลิกบิตแรกหลังจากสอง invocations มันพลิกสองบิตแรก การโยนบิตทั้งหมดใกล้กับส่วนเติมเต็มของ 2 ทำ แต่มันไม่ถูกต้อง
redtuna
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.