Bir Tamsayı Dizisini O (n) algoritmasıyla döndürme [kapalı]


10

Bir tamsayı dizisini belirli bir sayı k döndüren bir işlev yazın. k öğesinin sonundan başlayarak dizinin başlangıcına, diğer tüm öğelerin de yer açmak için sağa hareket etmesi gerekir.

Rotasyon yerinde yapılmalıdır.

Algoritma O (n) değerinden fazla çalışmamalıdır, burada n dizinin boyutudur.

Ayrıca işlemi gerçekleştirmek için sabit bir bellek kullanılmalıdır.

Örneğin,

dizi arr = {1, 2, 3, 4, 5, 6, 7, 8, 9} elemanlarıyla başlatılırsa

döndür (arr, 3) öğelerinin {7, 8, 9, 1, 2, 3, 4, 5, 6} olmasını sağlar

rotate (arr, 6) {4, 5, 6, 7, 8, 9, 1, 2, 3} ile sonuçlanır


1
Burada sabit hafıza ile ne kastedilmektedir? Şüphesiz , işlenen diziyi saklamak için en az O (n) bellek gerektirir, bu O (1) bellek kullanımını imkansız hale getirir .
Ad Hoc Garf Hunter

2
Bu soruyu konu dışı olarak kapatmak için oy kullanıyorum çünkü nesnel bir birincil kazanma kriteri olmayan sorular konu dışıdır, çünkü hangi girişin kazanılması gerektiğine tartışmasız karar vermeyi imkansız hale getirirler. Bunun bir popülerlik yarışması olması için kesinlikle hiçbir neden yok.
James

Kapatmak için oy verdi. Gönderen popülerlik Yarışma wiki ( burada ), "hayati yerlerinde ne yapacağına karar girenler için özgürlük verir ve bu özgürlüğü kullanmalarını teşvik edici." Sorunu herhangi bir algoritmaya açık bırakmak, en azından bir popcon olarak çalıştığı ölçüde değil, böylesine basit bir meydan okuma için yaratıcılığı teşvik etmek olarak sayılmaz. Bu bir kod-golf mücadelesi olarak daha uygun olacaktır .
mbomb007

Yanıtlar:


18

C (104)

void reverse(int* a, int* b)
{
    while (--b > a) {
        *b ^= *a;
        *a ^= *b;
        *b ^= *a;
        ++a;
    }
}

void rotate(int *arr, int s_arr, int by)
{
    reverse(arr, arr+s_arr);
    reverse(arr, arr+by);
    reverse(arr+by, arr+s_arr);
}

minified:

v(int*a,int*b){while(--b>a){*b^=*a;*a^=*b;*b^=*a++;}}r(int*a,int s,int y){v(a,a+s);v(a,a+y);v(a+y,a+s);}

4
While loop koşulunu şu şekilde a <-- b
yazmış olmalısınız

Eskiden C programlarının popülerlik yarışmaları kazandığı bir zaman vardı ...
Anubian Noob

Sen en iyisin! Ne kadar zarif ve optimize edilmiş .. Bunu bit dizisi ile yapabilir misiniz?

9

APL (4)

¯A⌽B
  • A döndürülecek yerlerin sayısıdır
  • B döndürülecek dizinin adıdır

APL'nin gerçekten gerekli olup olmadığından emin değilim, ancak uygulamada (dahili) gördüm, bu orantılı Ave sabit bellek ile zaman alacaktı .


+1 golf bu olsaydı :)
Glenn Teitelbaum

Ancak yerinde yapmaz.
marinus

@marinus: Kesinlikle gördüğüm uygulamalarda da var.
Jerry Coffin

Bu nasıl bir işlev? Olabilir {⍵⌽⍨-⍺}ya {⌽⍺⌽⌽⍵}. NARS2000'de zarif bir şekilde yazılabilir ⌽⍢⌽.
Adam

5

İşte Colin'in fikrinin uzun soluklu bir C versiyonu.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int gcd(int a, int b) {
  int t;
  if (a < b) {
    t = b; b = a; a = t;
  }
  while (b != 0) {
    t = a%b;
    a = b;
    b = t;
  }
  return a;
}

double arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int s_arr = sizeof(arr)/sizeof(double);

/* We assume 1 <= by < s_arr */
void rotate(double *arr, int s_arr, int by) {
  int i, j, f;
  int g = gcd(s_arr,by);
  int n = s_arr/g;
  double t_in, t_out;

  for (i=0; i<g; i++) {
    f = i;
    t_in = arr[f + s_arr - by];
    for (j=0; j<n; j++) {
      t_out = arr[f];
      arr[f] = t_in;
      f = (f + by) % s_arr;
      t_in = t_out;
    }
  }
}

void print_arr(double *arr, int s_arr) {
  int i;
  for (i=0; i<s_arr; i++) printf("%g ",arr[i]);
  puts("");
}

int main() {
  double *temp_arr = malloc(sizeof(arr));
  int i;

  for (i=1; i<s_arr; i++) {
    memcpy(temp_arr, arr, sizeof(arr));
    rotate(temp_arr, s_arr, i);
    print_arr(temp_arr, s_arr);
  }
}

Sabit bir bellek çözümü gibi görünmüyor, değil mi?
microbian

Evet, sabit bir bellek çözümüdür. "Bozuk" şeyler dizinin geçici bir kopyasıdır, böylece orijinal verileri tekrar tekrar kopyalayabilirim, böylece farklı döndürme miktarlarını test edebilirim.
Stephen Montgomery-Smith

Gerçek döndürme işlevi "döndürme" işlevidir. 5 tamsayı ve iki çift kullanır. Ayrıca bir tamsayı kullanan ve en çok O (log (n)) işlemi kullanan "gcd" işlevini çağırır.
Stephen Montgomery-Smith

Anladım. Cevabınızı yükselttim.
microbian

@ StephenMontgomery-Smith - bu O(log(n))işlemler nasıl . by1'e bakın, `j 'döngünüz s_arr / g veya N - bu O (N) işlemleri
Glenn Teitelbaum

3

C

Kriterlerin ne olduğundan emin değilim, ancak algoritma ile eğlendiğim için, işte girişim:

void rotate(int* b, int size, int shift)
{
    int *done;
    int *p;
    int i;
    int saved;
    int c;

    p = b;
    done = p;
    saved = *p;
    for (i = 0; i < size; ++i) {
        c = saved;
        p += shift;
        if (p >= b+size) p -= size;
        saved = *p;
        *p = c;
        if (p == done) {
            p += 1;
            done = p;
            saved = *p;
        }
    }
}

Ben de iyi bir önlem için golf; 126 bayt, daha kısa yapılabilir:

void r(int*b,int s,int n){int*d,*p,i,t,c;d=p=b;t=*p;for(i=0;i<s;++i){c=t;p+=n;if(p>=b+s)p-=s;t=*p;*p=c;if(p==d){d=++p;t=*p;}}}

3

Burada çok fazla C ++ çözümü görmüyorum, bu yüzden karakterleri saymadığı için bunu deneyeceğim.

Bu gerçek "yerinde" dönüştür, bu nedenle 0 ekstra alan kullanır (teknik olarak takas ve 3 inç hariç) ve döngü tam olarak N olduğundan, O (N) karmaşıklığını da yerine getirir.

template <class T, size_t N>
void rot(std::array<T,N>& x, int shift)
{
        size_t base=0;
        size_t cur=0; 
        for (int i = 0; i < N; ++i)
        {
                cur=(cur+shift)%N; // figure out where we are going
                if (cur==base)     // exact multiple so we have to hit the mods when we wrap
                {
                        cur++;
                        base++;
                }
                std::swap(x.at(base), x.at(cur)); // use x[base] as holding area
        }
}

Not: Bilerek kullanmadım, std::rotateçünkü bu tür bir yenilgi amacı
Glenn Teitelbaum

2

Olası dönme döngülerinin her birini sırayla n ile gerçekleştirirseniz (bunların GCD'si (n, len (arr)) vardır), bir dizi öğesinin tek bir geçici kopyasına ve birkaç durum değişkenine ihtiyacınız vardır. Bunun gibi, Python'da:

from fractions import gcd

def rotate(arr, n):
    total = len(arr)
    cycles = gcd(n, total)
    for start in range(0, cycles):
        cycle = [i % total for i in range(start, abs(n * total) / cycles, n)]
        stash = arr[cycle[-1]]
        for j in reversed(range(1, len(cycle))):
            arr[cycle[j]] = arr[cycle[j - 1]]
        arr[cycle[0]] = stash

1
Sanırım doğru fikre sahipsiniz, ancak cycledeğişkeniniz sabit olmayan boyutta. Bu diziyi gittikçe oluşturmanız gerekir.
Keith Randall

2

C (137 karakter)

#include <stdio.h>

void rotate(int * array, int n, int k) {
    int todo = (1<<n+1)-1;
    int i = 0, j;
    int tmp = array[0];

    while (todo) {
        if (todo & 1<<i) {
            j = (i-k+n)%n;
            array[i] = todo & 1<<j ? array[j] : tmp;
            todo -= 1<<i;
            i = j;
        } else tmp = array[++i];
    }
}

int main() {
    int a[] = {1,2,3,4,5,6,7,8,9};
    rotate(a, 9, 4);
    for (int i=0; i<9;i++) printf("%d ", a[i]);
    printf("\n");
}

rotate137 karaktere küçültülmüş işlev :

void r(int*a,int n,int k){int m=(1<<n+1)-1,i=0,j,t=a[0];while(m)if(m&1<<i){j=(i-k+n)%n;a[i]=(m&1<<j)?a[j]:t;m-=1<<i;i=j;}else t=a[++i];}

2

Faktör, dönebilen diziler için yerleşik bir türe sahiptir <circular>, bu yüzden bu aslında bir O (1) işlemidir:

: rotate ( circ n -- )
    neg swap change-circular-start ;

IN: 1 9 [a,b] <circular> dup 6 rotate >array .
{ 4 5 6 7 8 9 1 2 3 }
IN: 1 9 [a,b] <circular> dup 3 rotate >array .
{ 7 8 9 1 2 3 4 5 6 }

Ben Voigt'in etkileyici C çözümünün daha az aldatıcı bir Faktör eşdeğeri:

: rotate ( n s -- ) 
    reverse! swap cut-slice [ reverse! ] bi@ 2drop ;

IN: 7 V{ 0 1 2 3 4 5 6 7 8 9 } [ rotate ] keep .
V{ 3 4 5 6 7 8 9 0 1 2 }

2

JavaScript 45

Çünkü golf gibi zaten golf için gitti. t<= Dizinin boyutu olduğu sürece maksimum O (N) değerindedir .

function r(o,t){for(;t--;)o.unshift(o.pop())}

tO (N) cinsinden herhangi bir oranın üstesinden gelmek için aşağıdakiler kullanılabilir (58 karakter ağırlığında):

function r(o,t){for(i=t%o.length;i--;)o.unshift(o.pop())}

Geri dönmez, diziyi yerinde düzenler.


1
+1 içinr(o,t) => rot
Conor O'Brien

1

REBEL - 22

/_(( \d+)+)( \d+)/$3$1

Girdi: k _, basamak olarak bir tekli tamsayı , ardından bir boşluk, sonra bir boşlukla sınırlanmış tamsayı dizisi olarak ifade edilir.

Çıktı: Bir boşluk, sonra dizi döndürüldü.

Misal:

___ 1 2 3 4 5/_(( \d+)+)( \d+)/$3$1

Son durum:

 3 4 5 1 2

Açıklama:

Her yinelemede, bu yerine geçer _ve bir dizi [array] + tailile tail + [array].

Misal:

___ 1 2 3 4 5
__ 5 1 2 3 4
_ 4 5 1 2 3
 3 4 5 1 2

Bunun O (n) olduğunu sanmıyorum. Bir dizi kopyalamaktır O(n)ve siz de bunu yaparsınız n.
Ben Voigt

1

Java

public static void rotate(int[] arr, int by) {
    int n = arr.length;
    int i = 0;
    int j = 0;
    while (i < n) {
        int k = j;
        int value = arr[k];
        do {
            k = (k + by) % n;
            int tmp = arr[k];
            arr[k] = value;
            value = tmp;
            i++;
        } while (k != j);
        j++;
    }
}

Burada demo .

Küçültülmüş Javascript, 114 :

function rotate(e,r){n=e.length;i=0;j=0;while(i<n){k=j;v=e[k];do{k=(k+r)%n;t=e[k];e[k]=v;v=t;i++}while(k!=j);j++}}

1

Haskell

Bu aslında θ (n), çünkü bölünme θ (k) ve birleştirme θ (nk). Bellek hakkında emin değilim.

rotate 0 xs = xs
rotate n xs | n >= length xs = rotate (n`mod`(length xs)) xs
            | otherwise = rotate' n xs

rotate' n xs = let (xh,xt) = splitAt n xs in xt++xh

1

Python 3

from fractions import gcd
def rotatelist(arr, m):
    n = len(arr)
    m = (-m) % n # Delete this line to change rotation direction
    for i0 in range(gcd(m, n)):
        temp = arr[i0]
        i, j = i0, (i0 + m) % n
        while j != i0:
            arr[i] = arr[j]
            i, j = j, (j + m) % n
        arr[i] = temp

Sabit bellek
O (n) zaman karmaşıklığı



0

piton

   import copy
    def rotate(a, r):
        c=copy.copy(a);b=[]
        for i in range(len(a)-r):   b.append(a[r+i]);c.pop();return b+c

Dizinin kopyalanması sabit alan değildir. @ MadisonMay'in yanıtı, daha az karakter içeren bu kodla aynı şeyi yapar.
Blckknght

0

vb.net O (n) (Sabit bellek değil)

Function Rotate(Of T)(a() As T, r As Integer ) As T()     
  Dim p = a.Length-r
  Return a.Skip(p).Concat(a.Take(p)).ToArray
End Function

0

Yakut

def rotate(arr, n)
  arr.tap{ (n % arr.size).times { arr.unshift(arr.pop) } }  
end

0

C (118)

Muhtemelen bazı özellikleri ile biraz fazla yumuşaktı. Belleği orantılı kullanır shift % length. Negatif bir kaydırma değeri geçilirse ters yönde de dönebilir.

r(int *a,int l,int s){s=s%l<0?s%l+l:s%l;int *t=malloc(4*s);memcpy(t,a+l-s,4*s);memcpy(a+s,a,4*(l-s));memcpy(a,t,4*s);}

0

Python 2, 57

def rotate(l,n):
 return l[len(l)-n:len(l)]+l[0:len(l)-n]

Sadece l[-n:len(l)-n]beklediğim gibi çalıştı. Sadece []bir sebepten dolayı geri döner .


0
def r(a,n): return a[n:]+a[:n]

Birisi bunun gerçekten gereksinimleri karşılayıp karşılamadığını kontrol edebilir mi? Bence öyle, ama henüz CS okumadım (henüz).


0

C ++, 136

template<int N>void rotate(int(&a)[N],int k){auto r=[](int*b,int*e){for(int t;--e>b;t=*b,*b++=*e,*e=t);};r(a,a+k);r(a+k,a+N);r(a,a+N);}

0

Java

Son k öğelerini ilk k öğesiyle değiştirin ve sonra geri kalan öğeleri k ile döndürün. Sonunda k öğesinden daha az öğe kaldığında, kalan öğelerin% k sayısı kadar döndürün. Yukarıdaki kimsenin bu yaklaşımı benimsediğini sanmıyorum. Her eleman için tam olarak bir takas işlemi gerçekleştirir, her şeyi yerinde yapar.

public void rotate(int[] nums, int k) {
    k = k % nums.length; // If k > n, reformulate
    rotate(nums, 0, k);
}

private void rotate(int[] nums, int start, int k) {
    if (k > 0) {
        if (nums.length - start > k) { 
            for (int i = 0; i < k; i++) {
                int end = nums.length - k + i;
                int temp = nums[start + i];
                nums[start + i] = nums[end];
                nums[end] = temp;
            }
            rotate(nums, start + k, k); 
        } else {
            rotate(nums, start, k % (nums.length - start)); 
        }
    }
}

0

Perl 5 , 42 bayt

sub r{$a=pop;map{unshift@$a,pop@$a}1..pop}

Çevrimiçi deneyin!

Altyordam, birinci parametre olarak döndürülecek mesafeyi ve ikinci parametre olarak diziye bir referansı alır. Çalışma süresi dönüş mesafesine göre sabittir. Dizi boyutu çalışma süresini etkilemez. Dizi, bir öğeyi sağdan kaldırarak ve sola koyarak değiştirilir.

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.