Tasarım fonksiyonu f (f (n)) == -n


841

Son röportajımda aldığım bir soru:

Bir işlev tasarlayın f, öyle ki:

f(f(n)) == -n

n32 bit işaretli tam sayı nerede ; karmaşık sayılar aritmetiği kullanamazsınız.

Böyle bir işlevi tüm sayı aralığı için tasarlayamıyorsanız, mümkün olan en geniş aralık için tasarlayın.

Herhangi bir fikir?


2
Bu röportaj hangi iş içindi?
timtam

Yanıtlar:


377

Nasıl olur:

f (n) = işaret (n) - (-1) n * n

Python'da:

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 tamsayıları otomatik olarak rastgele uzunluk uzunluğuna yükseltir. Diğer dillerde, en büyük pozitif tamsayı taşacak, bu yüzden bunun dışındaki tüm tamsayılar için çalışacaktır.


Gerçek sayılar için çalışmasını sağlamak için size değiştirmeniz gerekiyor n içinde (-1) n ile { ceiling(n) if n>0; floor(n) if n<0 }.

C # 'da (taşma durumları hariç herhangi bir çift için çalışır):

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 için kırıldı, çünkü -1 * 0 hala 0
Joel Coehoorn

3
Hayır değil. f (1) 0 f (0) = 1 =
1800 bilgi

5
Ancak 1 için kırıldı. f (1) = 0. f (0) = 1
1800 BİLGİ

18
Hmm, çift ve tek sayılarla durumdan tasarruf etmeyi düşünmeliydim.
Bilinmiyor

38
Bence en önemli şey gerçek işlev değil (sonsuz sayıda çözüm var), ancak böyle bir işlevi inşa edebileceğiniz süreç.
pyon

440

Ne tür bir dil beklediklerini söylemediniz ... İşte statik bir çözüm (Haskell). Temel olarak en önemli 2 bitle uğraşıyor:

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

Dinamik bir dilde (Python) çok daha kolay. Argümanın X numarası olup olmadığını kontrol edin ve -X döndüren bir lambda döndürün:

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

23
Güzel, bunu seviyorum ... JavaScript'te aynı yaklaşım: var f = function (n) {return (typeof n == 'function')? n (): function () {return -n; }}
Mark Renouf

Muhtemelen Haskell'im çok paslı, ama (f 0) için kontrol ettin mi? Görünüşe göre (f 0x80000000) ile aynı sonucu üretmelidir, en azından sarmalama aritmetiği ile 32-bitlik girişlerle uğraşıyorsak (olumsuz işlemde). Ve bu kötü olurdu.
Darius Bacon

11
Ortalama Görüşmeci bile lambda yapısı nedir bilir mi olduğunu ?
Jeremy Powell

4
Tabii ki, böyle bir hile hilesi, statik olmasına rağmen Haskell'de de çalışır 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
Ne? Nerede f (n) === 'function' türünün, özellikle n'nin bir sayı olduğu ve bir sayının döndürülmesini beklediğiniz fikrini nereden buldunuz? Burada bir davanın nasıl uygulanabileceğini anlamıyorum. Python iyi konuşamıyorum, ancak JS'de bir işlev türü için bağımsız değişken denetimi bu durumda düz yanlıştır. Burada sadece sayısal çözüm geçerlidir. f bir işlevdir, f (n) sayıdır.
Harry

284

Ek bilgi kullanmıyorsa (32 bit int hariç), böyle bir işlevin neden tüm sayılar için var olamayacağının bir kanıtı:

F (0) = 0. olmalıdır. (İspat: diyelim ki f (0) = x. Sonra f (x) = f (f (0)) = -0 = 0. Şimdi, -x = f (f (x )) = f (0) = x, yani x = 0 anlamına gelir.)

Ayrıca, herhangi biri için xve yvarsayalım f(x) = y. O zaman istiyoruz f(y) = -x. Ve f(f(y)) = -y => f(-x) = -y. Özetlemek gerekirse: if f(x) = y, then f(-x) = -y, ve f(y) = -x, ve f(-y) = x.

Bu nedenle, 0 dışındaki tüm tam sayıları 4'lü kümelere ayırmalıyız, ancak bu sayıda tam sayıya sahibiz; sadece bu değil, pozitif karşılığı olmayan tamsayıyı kaldırırsak, hala 2 (mod4) numaramız var.

Kalan 2 maksimum sayıyı (abs değeriyle) kaldırırsak, işlevi alabiliriz:

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

Tabii ki başka bir seçenek, 0'a uymamak ve kaldırdığımız 2 sayıyı bonus olarak almaktır. (Ama bu sadece aptalca.)


29
Global değişkenlere veya kodu gizleyen hilelere başvurmadan negatif sayıları ele alan iyi bir prosedür çözümü bulmak için bu kadar aşağı okumak zorunda olduğuma inanamıyorum. Seni bir kereden fazla oylayabilirsem, yapardım.
Kyle Simek

Herhangi bir n imzalı bitte tek sayıda sıfır olmayan tam sayı olduğunu gözlemlemek güzel .
Andres Jaan Tack

Bu da benim cevabım olurdu, ancak kenar durumdan n = -2147483648(minimum değer) sakının ; abs(n)bu durumda yapamazsınız ve sonuç tanımsız (veya bir istisna) olacaktır.
Kirk Broadhurst

1
@ a1kmm: Üzgünüz, yukarıdaki -2³² -2³¹ olmalıdır. Her neyse, f (0) ≠ 0 (ve böylece f (0) = - 2³¹) olduğu durumda, aslında bu ikisinin diğerlerinden ayrıldığını gösterdiğimiz için daha kolay bir durumdur. Dikkate almamız gereken diğer durum f (0) = 0, ancak bazı x ≠ 0, x ≠ -2³¹ için f (x) = - 2³¹. Bu durumda, f (-2³¹) = f (f (x)) = - x (not -x -2³¹ olamaz, çünkü böyle bir x yoktur). Ayrıca f (-x) = y olsun. Sonra f (y) = f (f (-x)) = x. Yine y -2³¹ olamaz (f (y) = x olarak, ancak f (-2³¹) = - x ve x 0 değildir). Yani, -2³¹ = f (x) = f (f (y)) = - y, ki bu imkansız. Bu nedenle, gerçekten 0 ve -2 ile diğerlerinin bağlantısı kesilmelidir (başka hiçbir şeyin görüntüsü değil).
ShreevatsaR

1
@will İmzalı sıfır yok, eğer varsayalım ki ikisinin tamamlayıcı 32 bit tam sayılarından bahsediyorsak.
goffrie

146

C ++ 'da aşırı yükleme sayesinde:

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
Ne yazık ki, ad yönetimi nedeniyle, "f" olarak adlandırdığınız işlevlerin aslında garip adları vardır.
pyon

1
Böyle bir şey düşündüm, ama C'de düşünerek bu atıldı ... iyi iş!
Liran Orevi

@Rui Craverio: Yazar var anahtar sözcüğünü değişken adı olarak kullanmayı seçtiği için .NET 3.5 ve sonraki sürümlerinde çalışmaz.
Kredns

72
teknik olarak ... sorunun talep ettiği bu değil. f (int) ve f (float) olmak üzere 2 f () fonksiyonu tanımladınız ve sorular "f () fonksiyonunu tasarlayın ..." diye soruyor
elcuco

2
@elcuco Teknik olarak, elbette, ancak mantıksal olarak çoklu aşırı yüklere sahip bir işlevdir (bununla f (f (42)) yapabilirsiniz). Tanım, parametreler ve dönüş değeri hakkında hiçbir şey söylemediğinden, teknik bir tanım olarak kabul edemiyorum.
Marek Toman

135

Veya önişlemciyi kötüye kullanabilirsiniz:

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

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

O zaman Konrad "Le Chiffre" Rudolph olur muydun? Ceketimi alacağım. Evet, tüm "boş ana" şeyi biliyorum, ama bir "dönüş 0;" çok fazla çaba ;-)
Skizz

25
@Skizz, int dönüş değeri ile bile c ++ 'da main'den 0 döndürmesi gerekmez ... bu yüzden doğru yaparak aslında bir tane daha az karakter yazıyorsunuz!
Dan Olson

10
Skizz her zaman ön işlemciyi kötüye kullanıyor: D
Arnis Lapsa

23
Bu bir işlev değil .. yani bu geçerli bir çözüm değil
smerlin

3
@smerlin: Teknik olarak satır içi işlev döndüren bir satır içi işlev: her ikisinin gövdeleri derleme zamanında veya daha önce genişletilir. Bundan daha verimli olamaz.
Jon Purdy

103

Bu, tüm negatif sayılar için geçerlidir.

    f (n) = abs (n)

İki tamamlayıcı tamsayı için pozitif sayılardan bir tane daha negatif sayı olduğu için , aynı ile aynı çözümden f(n) = abs(n)bir tane daha durum için geçerlidir . Birini yakaladım ...: Df(n) = n > 0 ? -n : nf(n) = -abs(n)

GÜNCELLEME

Hayır, sadece bir durum için geçerli değil çünkü az önce litb'nin yorumuyla tanıştım ... abs(Int.Min)sadece taşacak ...

Ben de mod 2 bilgilerini kullanmayı düşündüm, ama sonuçta, işe yaramadı ... erken. Doğru yapılırsa Int.Min, taşması dışında tüm sayılar için çalışır .

GÜNCELLEME

Bir süre onunla oynadım, güzel bir manipülasyon hilesi arıyorum, ancak mod 2 çözümü bir tanesine uyurken güzel bir tek astar bulamadım.

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

C # 'da bu şu olur:

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

Tüm değerler için çalışan almak için, değiştirmek zorunda Math.Abs()olan (n > 0) ? +n : -nve bir in hesaplama dahil uncheckedblokta. Sonra Int.Minda kontrolsüz olumsuzlama gibi kendi kendine haritalanır.

GÜNCELLEME

Başka bir cevaptan esinlenerek işlevin nasıl çalıştığını ve böyle bir işlevi nasıl oluşturacağınızı açıklayacağım.

En baştan başlayalım. Fonksiyon f, nbir dizi değer veren belirli bir değere tekrar tekrar uygulanır .

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

Soru , argümanın f(f(n)) = -nardışık iki olumsuz uygulamasıdır f. Diğer fdört uygulama - toplamda dört - tekrar ortaya çıkan argümanı yeniden reddetmektedir n.

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

Şimdi dört uzunluklu bariz bir döngü var. x = f(n)Elde edilen denklemin ikame edilmesi ve tutulması f(f(f(n))) = f(f(x)) = -x, aşağıdakileri verir.

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

Böylece iki sayı ve dört sayı ile birlikte dört uzunluklu bir döngü elde ederiz. Döngüyü dikdörtgen olarak hayal ediyorsanız, reddedilen değerler karşıt köşelerde bulunur.

Böyle bir döngü oluşturmak için birçok çözümden biri, n'den başlayarak aşağıdakilerdir.

 n => birini reddet ve çıkar
-n - 1 = - (n + 1) => bir tane ekle
-n => olumsuzlama ve bir tane ekleme
 n + 1 => birini çıkart
 n

Somut bir örnek böyle bir döngüdür +1 => -2 => -1 => +2 => +1. Neredeyse bitti. İnşa edilen döngünün tek bir pozitif sayı içerdiğini, hatta ardılı olduğunu ve her iki sayı da olumsuzladığını belirterek, tam sayıları bu tür döngülere kolayca bölebiliriz ( 2^32dördün katıdır) ve koşulları sağlayan bir işlev bulduk.

Ama sıfırla ilgili bir sorunumuz var. Döngü içermelidir 0 => x => 0çünkü sıfır kendi kendine reddedilir. Ve döngü halihazırda 0 => xbunu izlediği için 0 => x => 0 => x. Bu sadece iki uzunluklu bir döngüdür ve xiki uygulamadan sonra kendi içine dönüşür, içine dönüşmez -x. Neyse ki sorunu çözen bir vaka var. Eğer Xeşittir sıfır biz sadece sıfır içeren tek üyeli bir döngü elde etmek ve sıfır sabit nokta olduğunu biz bu sorun YAPILMASINA çözüldü f.

Bitti? Neredeyse. Biz 2^32sıfır bırakarak sabit bir nokta, sayıları 2^32 - 1sayı ve dört sayı döngü içine bu numarayı bölüm gerekmektedir. Kötü 2^32 - 1, dördün katı değildir - dört uzunluktaki herhangi bir döngüde olmayan üç sayı kalır.

Ben arasında değişen 3 bitlik imzalı itegers küçük kümesi kullanılarak çözeltinin geri kalan kısmını açıklayacağız -4için +3. Sıfırla işimiz bitti. Bir tam döngümüz var +1 => -2 => -1 => +2 => +1. Şimdi bize döngüsü başlayan inşa edelim +3.

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

Ortaya çıkan sorun, +43 bit tam sayı olarak gösterilememesidir. Biz elde ediyorum +4olumsuzlaştırılmasıyla -3için +3hala geçerli 3 bitlik tamsayı nedir - - ama sonra bir eklenerek +3(ikili 011) verir 100ikili. İşaretsiz tam sayı olarak yorumlanır, +4ancak işaretli tam sayı olarak yorumlamamız gerekir -4. Yani aslında -4bu örnek için veya Int.MinValuegenel durumda tamsayı aritmetik olumsuzlamanın ikinci bir sabit noktasıdır - 0 ve Int.MinValuekendileriyle eşleştirilir. Yani döngü aslında aşağıdaki gibidir.

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

Bu iki uzunluklu bir döngüdür ve ek +3olarak döngüye girer -4. Sonuç olarak -4, iki işlev uygulamasından sonra +3doğru şekilde eşlenir, -3iki işlev uygulamasından sonra doğru olarak eşlenir , ancak -3iki işlev uygulamasından sonra hatalı olarak eşlenir.

Bu yüzden biri hariç tüm tamsayılar için çalışan bir fonksiyon oluşturduk. Daha iyisini yapabilir miyiz? Hayır yapamayız. Neden? Dört uzunluktaki döngüleri inşa etmeliyiz ve dört değere kadar tam sayı aralığını kapsayabiliriz. Kalan değerler iki sabit noktadır 0ve Int.MinValuekendileri ve iki rastgele tamsayı xile eşlenmelidir ve -xbu iki işlev uygulaması tarafından birbirine eşlenmelidir.

Eşlemek için xiçin -xve ters bir dört devir oluşturmak içermesi ve bu döngü zıt köşelerinde yer almalıdır tam tersi. Sonuç olarak 0ve Int.MinValuede karşıt köşeleri olması gerekir. Bu , iki sabit noktayı ve iki işlev uygulamasından sonra doğru bir şekilde eşler xve -xdeğiştirir ve bizi iki hatalı girişle bırakır. Bu nedenle, tüm değerler için çalışan bir işlev oluşturmak mümkün değildir, ancak biri hariç tüm değerler için çalışan bir işlevimiz vardır ve bu elde edebileceğimiz en iyisidir.0Int.MinValue


Kriterleri karşılamıyor: abs (abs (n))! = -N
Dan Olson

Elbette, söylediği gibi, tüm negatif sayılar için. Bu sorunun bir parçasıydı: Eğer genel bir soru bulamazsanız, mümkün olan en geniş aralık için çalışan bir soru ortaya koyun.
jalf

Bu cevap en azından Marj Synowiec ve Rowland Shaw'un yanıtı kadar iyi, sadece farklı bir sayı aralığı için çalışıyor
1800 BİLGİ

19
Dostum, sadece "GÜNCELLEME" 'lerden kurtulabilir ve sadece bir tane doğru cevap yazabilirsiniz. Alt 3/4 ("başka bir cevap esinlenerek") harika.
Andres Jaan Tack

1
Negatif sayılar için abs-çözümü çok seviyorum. Basit ve kolay anlaşılır.
Thorbjørn Ravn Andersen

97

Karmaşık sayıları kullanarak, bir sayıyı reddetme görevini etkili bir şekilde iki adıma bölebilirsiniz:

  • n ile i çarpılır ve saat yönünün tersine 90 ° döndürülmüş n * i elde edilir
  • tekrar i ile çarp, ve -n elde edersin

Harika bir şey, herhangi bir özel kullanım koduna ihtiyacınız olmamasıdır. Sadece i ile çarpmak işi yapar.

Ancak karmaşık sayılar kullanmanıza izin verilmez. Bu nedenle, veri aralığınızın bir bölümünü kullanarak bir şekilde kendi hayali ekseninizi oluşturmanız gerekir. Başlangıç ​​değerleri kadar hayali (ara) değerlere ihtiyaç duyduğunuzdan, veri aralığının sadece yarısı kalır.

İmzalı 8 bit veri varsayarak bunu aşağıdaki şekilde görselleştirmeye çalıştım. Bunu 32 bit tamsayılar için ölçeklendirmeniz gerekir. İlk n için izin verilen aralık -64 ila +63'tür. İşte pozitif n için fonksiyonun yaptığı:

  • N 0..63 (başlangıç ​​aralığı) içindeyse, işlev çağrısı 64 ekler ve n'yi 64..127 aralığına eşleştirir (ara aralık)
  • N 64..127 (ara aralık) içinse, işlev n değerini 64'ten çıkarır, n eşlemesi 0 aralığına çıkar.

Negatif n için, fonksiyon -65 ..- 128 ara aralığını kullanır.

alternatif metin


4
@geschema, bu güzel grafikleri oluşturmak için hangi aracı kullandınız?
jwfearn

10
Maalesef, soru açıkça karmaşık sayılar yok diyor.
Rui Craveiro

6
@Liran: OmniGraffle (yalnızca Mac) kullandım
geschema

39
+1 Bence bu en iyi cevap. İnsanların yeterince okuduğunu sanmıyorum, çünkü hepsi sorunun karmaşık sayıların kullanılamayacağını söyledi. Her şeyi okudum ve sorulan soruya karmaşık olmayan çözüm için zemin hazırlamak için çözümü karmaşık sayılarla tanımladınız. Çok iyi yapmışsın.
jrista

1
@jrista tüm çözümlerde 'karmaşık sayılar' gerçekten olan ikinci bir boyut kullanılır (en çok tek, çift ve yukarıdaki kullanımlar floatvs int). Birçok cevabın açıkladığı '4 element halkası', her biri 2 durumlu 2 boyut olarak gösterilebilen 4 durum gerektirir. Sorun bu cevap o ek işlem alanı gerektirir olmasıdır (-64..63 için sadece 'eserler', henüz ihtiyaçlar -128..127 boşluk) ve açıkça formüle yazılı açıkça belirtin yok!
Kirk Broadhurst

65

İnt.MaxValue ve int.MinValue hariç çalışır

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

resimli


Bunun neden reddedildiğinden emin değilim. Hangi girdiler için başarısız?
Rodrick Chapman

Neden signum fonksiyonunu kullanmıyorsunuz?!?
comonad

1
Görüntü gerçekten çok iyi. Gönder 0etmek 0ve -2147483648karşı -2147483648, olumsuzluk operatör için bu iki sayının sabit beri noktaları x => -x. Diğer sayılar için yukarıdaki resimdeki okları takip edin. SurDin'in cevabından ve yorumlarından da anlaşılacağı gibi, bu durumda iki sayı olacak 2147483647ve -2147483647başka bir çiftin takası kalmayacak.
Jeppe Stig Nielsen

Bir surat gibi görünüyor - çok fazla kırışıklık ile
Anshul

48

Soru giriş tipi ve işlevin dönüş değeri hakkında hiçbir şey söylemez folmak zorunda (en azından değil bunu takdim ettik yolu) ...

... sadece n 32 bitlik bir tam sayı olduğunda f(f(n)) = -n

Peki, böyle bir şeye ne dersin

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

N 32 bitlik bir tamsayı ise f(f(n)) == -n, ifade true olur.

Açıkçası, bu yaklaşım daha da geniş bir sayı aralığında çalışmak üzere genişletilebilir ...


2
Sinsi. Karakter sınırı.
Joe Phillips

2
Evet, benzer bir yaklaşım üzerinde çalışıyordum. Yine de beni dövüyorsun. +1 :)
jalf

1
Çok zeki! Bu, açık ve ideal bir çözüm olan karmaşık sayıları kullanmaya çok yakındır (ve etkili bir şekilde aynıdır), ancak açıkça izin verilmez. İzin verilen sayı aralığının dışında çalışma.
Kirk Broadhurst

48

javascript (veya dinamik olarak yazılmış diğer diller) için, işlevin int veya nesneyi kabul etmesini ve diğerini döndürmesini sağlayabilirsiniz. yani

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

vererek

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

alternatif olarak aşırı yazmayı güçlü yazılan bir dilde kullanabilirsiniz, ancak bu kurallara aykırı olabilir.

int f(long n) {
    return n;
}

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

İkincisi, "a" (tekil) fonksiyonun gerekliliği anlamına gelmez. :)
Drew

Cevabın ikinci yarısını kaldırın ve bu doğru bir cevaptır.
jmucchiello

@ Bu yüzden kuralları ihlal edebileceğinden bahsettim
cobbal

2
JavaScript'te, bir işlev bir nesnedir ve durumu koruyabilir.
Nosredna

1
IMO: işlev f (n) {dönüş n.passed? -n.val: {val: n, geçti: 1}} daha okunabilir ve kısadır.
SamGoody

46

Platformunuza bağlı olarak, bazı diller işlevde durumu korumanıza izin verir. VB.Net, örneğin:

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

    Return n * flag
End Function

IIRC, C ++ buna da izin verdi. Yine de farklı bir çözüm aradıklarından şüpheleniyorum.

Başka bir fikir, işleve ilk çağrının sonucunu tanımlamadığı için, işareti ters çevirip çevirmeyeceğinizi kontrol etmek için tek / eşitliği kullanabileceğinizdir:

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

Tüm çift sayıların büyüklüğüne bir tane ekleyin, tüm tek sayıların büyüklüğünden birini çıkarın. İki çağrının sonucu aynı büyüklüğe sahiptir, ancak işareti takas ettiğimiz tek çağrı. Bunun işe yaramayacağı bazı durumlar vardır (-1, maks veya min int), ancak şimdiye kadar önerilenlerden daha iyi çalışıyor.


1
Ben her zaman garip çünkü MAX_INT için çalışıyor inanıyorum. MIN_INT ve -1 için çalışmaz.
Airsource Ltd

9
Yan etkileri varsa bir işlev değildir.
nos

12
Bu matematikte doğru olabilir, ancak programlamada önemsizdir. Yani soru, matematiksel bir çözüm mü yoksa programlama çözümü mü aradığıdır. Ama bunun bir programlama işi olduğu düşünüldüğünde ...
Ryan Lundy

+1 "statik int x" ile bir FIFO çıktının reddedilmesi ile bir tane yayınlayacaktım. Ama bu yeterince yakın.
phkahler

2
@ nos: Evet, referans olarak şeffaf değil.
Clark Gaebel

26

JavaScript istisnalarını kullanma.

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

f(f(0)) => 0

f(f(1)) => -1


İstisnalar daha önce böyle kullanıldığından şüpheliyim ... :)
NoBugs

+1 Kutunun dışında düşünmek. Güzel! Ama üretim kodunda güvenli olmak için typeof kullanırdım.

21

Tüm 32 bit değerler için (-0, -2147483648 olan uyarı ile)

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

Temel olarak her -x => x => -x döngüsünü ay => -y => y döngüsüyle eşleştirmeniz gerekir. Ben de onun karşıt taraflarını eşleştirdim split.

4 bitlik tamsayılar için:

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

21

Muhtemelen kuralları bir şekilde büken ancak tüm sayısal tipler (şamandıralar, ints, çiftler) ve hatta tekli eksi aşırı yüklenen sınıf tipleri için çalışan bir C ++ sürümü:

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

İyi bir fikir. Alternatif olarak, muhtemelen yapıyı kaybedebilir ve bunun yerine bir fonksiyonun bir işaretçi döndürmesini, diğer fonksiyonun dereference ve negate olmasını sağlayabilirsiniz.
Imbue

20

x86 asm (AT&T stili):

; 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

Kod kontrol edildi, tüm olası 32 bit tamsayılar geçti, -2147483647 (düşük akış) hatası.


19

Globalleri kullanıyor ... ama öyle mi?

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

3
Bunun soru askerinin niyeti olduğundan emin değil, ama "kutunun dışında düşünmek" için +1.
Liran Orevi

5
Koşullu olarak "done = true" demek yerine, her zaman "done =! Done" demelisiniz, bu şekilde işleviniz birden fazla kullanılabilir.
Chris Lutz

@Chris, true olarak yapılan ayar bir if (! Done) bloğunun içinde olduğundan, done =! Done ile eşdeğerdir, ancak! Done'in hesaplanması (veya yeterince akıllıysa derleyici tarafından optimize edilmesi) gerekmez .
nsayer

1
İlk düşüncem de, bu özel soru için hile gibi hissedilmesine rağmen, küresel bir değişken kullanarak bunu çözmekti. Bununla birlikte, küresel değişken değişken çözümünün, söz konusu spesifikasyonlarda verilen en iyi çözüm olduğunu savunuyorum. Küresel bir şey kullanmak, neler olduğunu anlamayı kolaylaştırır. Ben bir tamamlandı! = Yapılır olsa daha iyi olacağını kabul ediyorum. Bunu if deyiminin dışına taşıyın.
Alderath

3
Teknik olarak, durumu koruyan her şey bir işlev değil, bir durum makinesidir. Tarafından tanımı , bir işlev her zaman aynı girişi için aynı çıkış verir.
Ted Hopp

19

Bu Perl çözümü tamsayılar, kayan noktalar ve dizgiler için çalışır .

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

Bazı test verilerini deneyin.

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

Çıktı:

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

Ama bu bir int tutmuyor. Temelde global değişken verileri int "n" 'nin kendisinde saklıyorsunuz ... ancak int değil, aksi halde bunu yapamazsınız. Örneğin n, 548'in "First_Time_548" haline gelebilmesi için bir dize olsaydı ve bir sonraki işlevde çalıştığında ... if (prefix == First_Time_ ")" First_Time_ "yerine" - "yazın
Albert Renshaw

@AlbertRenshaw Bu fikirleri nereden edindiğinizden emin değilsiniz. (1) Burada kesinlikle hiçbir global değişken yoktur. (2) İşleve bir int verirseniz, işleve tek sayıda çağırırsanız bir int geri veya int'e bir başvuru alırsınız. (3) Belki de en temelde bu Perl'dir . Tüm pratik amaçlar için, ints ve stringler tamamen değiştirilebilir. Sayılara benzeyen dizeler çoğu bağlamdaki sayılar gibi mükemmel bir şekilde işlev görür ve istendiğinde sayılar mutlu bir şekilde dizilir.
FMc

Maalesef çok perl bilmiyorum, küresel bir dizi kullanıyor
gibisin

18

Hiç kimse f (x) 'in aynı tipte olması gerektiğini söylemedi.

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


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

16

Ben aslında sorunun kendisi için bir çözüm vermeye çalışmıyorum, ama birkaç yorum var, soru bu sorunun ortaya atıldığı gibi (iş?) Röportajın bir parçası olduğunu belirtti:

  • Önce "Neden böyle bir işleve ihtiyaç duyulur? Bunun bir parçası olan daha büyük sorun nedir?" gerçek asıl sorunu yerinde çözmeye çalışmak yerine. Bu nasıl düşündüğümü ve bunun gibi sorunlarla nasıl başa çıktığımı gösterir. Kim bilir? Sorunun ilk etapta bir röportajda sorulmasının asıl nedeni bile bu olabilir. Eğer cevap "Boş verin, gerekli olduğunu varsayın ve bana bu işlevi nasıl tasarlayacağınızı gösterin." Daha sonra bunu yapmaya devam edeceğim.
  • : (Döngüyü bariz Sonra ben C # test durumu ben kullanırım kod yazmak istiyorum int.MinValueiçin int.MaxValue, ve her biri için nbu aralık çağrısında f(f(n))ve sonucu kontrol ediyor -nSonra böyle bir işleve ulaşmak için Test Driven Development kullanırsınız söylüyorum).
  • Ancak görüşmeci, ortaya çıkan sorunu çözmemi istemeye devam ederse, aslında bir çeşit cevap bulmaya çalışmak için görüşme sırasında sahte kodu denemeye ve karalamak isterdim. Ancak, eğer görüşmeci şirketin neye benzediğine dair herhangi bir gösterge olursa, işe girmek için atlayacağımı gerçekten sanmıyorum ...

Oh, bu cevap röportaj bir C # programlama ile ilgili pozisyon için olduğunu varsayar. Röportaj matematikle ilgili bir pozisyon için olsa elbette aptalca bir cevap olurdu. ;-)


7
Eğer 32 bit istedikleri için şanslısınız, eğer 64 bit olsaydı testleri yaptıktan sonra röportaj asla devam etmeyecektir ;-)
alex2k8

Gerçekten, eğer o testi gerçekten yazmak ve bir röportaj sırasında çalıştırmak için bir noktaya bile gelirsem. ;-) Benim açımdan: Bir röportajda o noktaya gelmemeye çalışırdım. Programlama, bence "kod satırlarını nasıl yazıyor" dan daha çok "bir düşünme şekli" dir.
peSHIr

7
Gerçek bir röportajda bu tavsiyeye uymayın. Görüşmeci sizden soruyu gerçekten cevaplamanızı bekliyor. Sorunun uygunluğunu sorgulamak size hiçbir şey satın almaz, ancak görüşmeci rahatsız edebilir. Önemsiz bir test tasarlamak, cevaba bir adım daha yaklaşmanıza neden olmaz ve röportajda çalıştıramazsınız. Ek bilgi (32 bit) alırsanız, bunun nasıl yararlı olabileceğini anlamaya çalışın.
Stefan Haustein

Daha fazla bilgi istediğimde (muhtemelen sorusunun süreçteki ilgisini sorgularken) rahatsız olan bir görüşmeci, birlikte çalışmak istediğim bir görüşmeci değildir. Bu yüzden röportajlarda böyle sorular sormaya devam edeceğim. Eğer beğenmezlerse, muhtemelen her iki zamanımızı da boşa harcamak için röportajı bitireceğim. "Ben sadece emirleri uyguluyordum" aklı bir bit set sevmiyorum. Yapıyor musun..?
peSHIr

16

En önemli 2 biti değiştirirdim.

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

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

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

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

Gördüğünüz gibi, taşınan biti dışarıda bırakarak sadece bir ek.

Cevabı nasıl buldum? İlk düşüncem sadece simetriye ihtiyaç duyuyordu. Başladığım yere geri dönmek için 4 dönüş. İlk başta, bu 2bits Gray kod düşündüm. Sonra standart ikili yeterli olduğunu düşündüm.


Bu yaklaşımdaki sorun, ikisinin iltifat negatif sayılarıyla (her modern CPU'nun kullandığı şey) işe yaramamasıdır. Bu yüzden aynı cevabımı sildim.
Tamas Czinege

Soru 32-bit işaretli tam sayıları belirtmiştir. Bu çözüm, iki bitli ya da 32 bitlik işaretli tam sayıların tamamlayıcı gösterimleri için çalışmaz. Sadece modern bilgisayarlarda (kayan nokta sayıları hariç) çok nadir görülen işaret ve büyüklük gösterimleri için çalışır.
Jeffrey L Whitledge

1
@DrJokepu - Vay be, altı ay sonra - jinx!
Jeffrey L Whitledge

Sayıları işlevin içindeki işaret ve büyüklük gösterimine dönüştürmeniz, dönüşümü gerçekleştirmeniz ve ardından döndürmeden önce yerel tamsayı gösterimine geri dönüştürmeniz gerekmez mi?
Bill Michell

Temelde hayali bir bit
ekleyerek

16

Burada, karmaşık sayıların bu sorunu çözmek için kullanılamayacağı gereksiniminden veya iddiadan esinlenen bir çözüm.

-1'in kare kökü ile çarpmak, -1'in tamsayıların üzerinde bir kare kökü olmadığı için başarısız gibi görünen bir fikirdir. Ancak mathematica gibi bir programla oynamak, örneğin

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

ve bu neredeyse -1 kareköküne sahip olmak kadar iyidir. İşlevin sonucunun işaretli bir tam sayı olması gerekir. Bu nedenle, 0'a en yakın olan x modulo n'ye y tamsayısını döndüren değiştirilmiş bir modulo işlemi mods (x, n) kullanacağım. Sadece çok az sayıda programlama dilinde bir modulo işlemi var, ancak kolayca tanımlanabilir . Örneğin, pitonda:

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

Yukarıdaki denklemi kullanarak, problem artık şu şekilde çözülebilir:

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

Bu f(f(x)) = -x, aralıktaki tüm tamsayıları karşılar . Sonuçları da bu aralıktadır, ancak elbette hesaplamanın 64 bit tamsayılara ihtiyacı olacaktır.[-231-2, 231-2]f(x)


13

2 ^ 32-1 sayılar aralığı için C #, (Int32.MinValue) hariç tüm int32 numaraları

    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

baskılar:

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

Bu aynı zamanda 1073741824 olan f (0) için de çalışmaz. F (1073741824) = 0. f (f (1073741824)) = 1073741824
Dinah

Genellikle herhangi bir bit boyutta bir ikinin tamamlayıcısı tamsayı türü için, işlev kanıtlayabilirim zorundadır en az iki giriş değerleri için değil iş.
slacker

12

Esasen, fonksiyonun mevcut aralığı 4 döngüsüne bölmek zorundadır, n döngüsünün karşı ucunda -n ile. Bununla birlikte, 0, aksi halde, boyut 1 döngüsünün bir parçası olmalıdır 0->x->0->x != -x. 0 tek başına olduğu için, ürün yelpazemizde (boyutu 4'ün katı olan) 3 elementle uygun bir döngüde olmayan 3 değer daha olmalıdır.

Ben olmak için bu ekstra garip değerleri seçti MIN_INT, MAX_INTve MIN_INT+1. Dahası, MIN_INT+1plan yapacak MAX_INTdoğru, ama orada takılıp ve geri eşlemeyin. Bence bu en iyi uzlaşmadır, çünkü doğru çalışmayan sadece aşırı değerlerin güzel özelliğine sahiptir. Ayrıca, tüm BigInts için işe yarayacağı anlamına gelir .

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

Kimse bunun vatansız olması gerektiğini söylemedi.

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

Hile, ancak çok sayıda örnek kadar değil. Arayanızın adresinin & f olup olmadığını görmek için yığını daha da kötüleştirmek olurdu, ancak bu daha taşınabilir olacaktır (iş parçacığı güvenli olmasa da ... iş parçacığı için güvenli sürüm TLS kullanacaktır). Daha da kötülük:

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

Tabii ki, bunların hiçbiri MIN_INT32 için çok iyi çalışmıyor, ancak daha geniş bir tür döndürmenize izin verilmedikçe, bu konuda yapabileceğiniz çok az şey var.


adresi sormak için 'yükseltebilirsiniz' (evet, ref \ ile bir işaretçi olarak almalısınız) - C'de, örneğin: int f (int & n) {statik int * addr = & n; eğer (addr == & n) {return -n; } geri dönüş n; }
IUnknownPointer

11

31. biti hayali ( i ) bit olarak toplam aralığın yarısını destekleyecek bir yaklaşım olarak düşünebilirim .


Bu daha karmaşık ama şu anki en iyi cevaptan daha etkili olmayacaktır
1800 BİLGİ

1
@ 1800 BİLGİ: Öte yandan, [-2 ^ 30 + 1, 2 ^ 30-1] alanı bitişiktir ve bu da matematiksel açıdan daha caziptir.
Jochen Walter

10

n = [0 .. 2 ^ 31-1] için çalışır

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

Sorun "32 bitlik imzalı tamsayı" devletler ama onlar olup olmadığını belirtmez ikiler-tamamlayıcı veya olanları-tamamlayıcısı .

Ones-complement kullanırsanız, 2 ^ 32 değerinin tümü dört uzunluktaki döngülerde gerçekleşir - sıfır için özel bir duruma ihtiyacınız yoktur ve ayrıca koşullara da ihtiyacınız yoktur.

C dilinde:

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

Bu, tarafından çalışır

  1. Yüksek ve düşük 16 bitlik blokların değiştirilmesi
  2. Bloklardan birini ters çevirme

İki geçişten sonra orijinal değerin bitsel tersine sahibiz. Bir-tamamlayıcı temsil hangi olumsuzlama eşdeğerdir.

Örnekler:

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
Farklı mimarilerde bayt sırası ne olacak?
Steven

1
Tüm aritmetik 32 bittir. Tek tek baytları manipüle etmiyorum, bu nedenle bayt sırası onu etkilemez.
finnw

Bu kulağa oldukça yakın geliyor. Girişin 2-tamamlayıcı olduğunu varsayabilirsiniz. Böylece işaret biti gösterimine dönüşürsünüz. Şimdi son bite bağlı olarak, ilk biti ve son biti ya da sadece son biti çevirirsiniz. Temel olarak sadece çift sayıları reddeder ve sürekli çift / tek dolaşırsınız. Böylece tuhaftan teke ve hatta 2 çağrıdan sonra bile geri dönersiniz. Sonunda tekrar 2-komplement'e dönüşürsünüz. Kodunu aşağıda bir yere gönderdim.
Stefan Haustein

9

: D

boolean inner = true;

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

5
Ayrıca, sizi oradaki röportajdan çıkarmazlarsa, küresel değişkenlerin neden kötü olduğu hakkında bir tartışma getirebilir!
palswim


7

Bir matematikçi olarak bu ilginç soruna bakış açımı paylaşmak istiyorum. Bence en verimli çözüme sahibim.

Doğru hatırlıyorsam, sadece ilk biti çevirerek işaretli bir 32 bit tamsayıyı reddedersiniz. Örneğin, n = 1001 1101 1110 1011 1110 0000 1110 1010 ise, -n = 0001 1101 1110 1011 1110 0000 1110 1010.

Öyleyse, imzalı bir 32 bit tamsayı alan ve başka bir imzalı 32 bit tamsayı döndüren f işlevini, iki kez f almanın ilk biti çevirmeyle aynı olduğunu nasıl tanımlarız?

Tamsayı gibi aritmetik kavramlardan bahsetmeden soruyu yeniden ifade edeyim.

Sıfırlar dizisi ve 32 uzunluktaki dizileri alan ve aynı uzunluktaki sıfır dizileri ve aynı uzunluktaki dizileri döndüren bir f işlevini nasıl tanımlarız, iki kere f alma özelliği ilk biti çevirmekle aynıdır?

Gözlem: Yukarıdaki soruyu 32 bit kasa için cevaplayabiliyorsanız, 64 bit kasa, 100 bit kasa vb. İçin de cevap verebilirsiniz. Sadece ilk 32 bit'e f uygularsınız.

Şimdi soruyu 2 bit durum için cevaplayabilirseniz, Voila!

Ve evet, ilk 2 biti değiştirmek yeterli.

İşte sözde kod

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.

Not: Adım 2 ve adım 3 birlikte (a, b) -> (-b, a) şeklinde yazılabilir. Tanıdık geliyor? Bu size uçağın 90 derecelik dönüşünü ve -1'in kare kökü ile çarpılmasını hatırlatmalıdır.

Eğer sadece uzun bir başlangıç ​​olmadan sahte kodu sadece sunsaydım, şapkadan bir tavşan gibi görünüyordu, çözümü nasıl aldığımı açıklamak istedim.


6
Evet bu ilginç bir problem. Matematiğini biliyorsun. Ancak bu bir bilgisayar bilimi problemidir. Yani bilgisayarları incelemeniz gerekiyor. İşaret büyüklüğünde temsile izin verilir, ancak yaklaşık 60 yıl önce modası geçmiştir. 2'nin tamamlayıcısı en popüler olanıdır.
Windows programcısı

5
Fonksiyonunuzun iki bite iki kez uygulandığında yaptığı şey: (a, b) -> (-b, a) -> (-a, -b). Ancak, (-a, -b) 'ye ulaşmaya çalışıyoruz (-a, -b).
buti-oxa

@ buti-oxa, haklısın. İki bit işlemi şöyle olmalıdır: 00 -> 01 -> 10 -> 11 -> 00. Ama sonra algoritmam, Windows programcısının dediği gibi şu anda popüler olmayan işaret-büyüklük gösterimini varsayar, bu yüzden algoritmamın çok az faydası olduğunu düşünüyorum .
Yoo

Yani adımları bir kez yerine iki kez yapamaz mı?
Nosredna

4
buti-oxa tamamen doğrudur: fonksiyon iki çağrmadan sonra ilk biti bile çevirmez, ilk iki biti çevirir. Tüm bitleri çevirmek, 2'nin tamamlayıcısının yaptıklarına daha yakındır, ancak tam olarak doğru değildir.
redtuna
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.