É um código OVSF?


27

Dada uma lista de 1s e -1s, determine se é ou não um código OVSF válido (emitindo um valor de verdade ou falsey).

Os códigos OVSF são definidos da seguinte maneira:

  • [1] é um código OVSF.

  • Se Xfor um código OVSF, então X ++ Xe X ++ -Xsão ambos códigos OVSF.

    Aqui ++está a concatenação da lista e -nega todos os elementos da lista.

  • Nenhuma outra lista é um código OVSF válido.

Você pode supor que a lista de entrada contenha apenas -1e 1, mas deve manipular a lista vazia corretamente, bem como listas cujo comprimento não seja 2.

O código mais curto (em bytes) vence.

Casos de teste

[] -> False
[1] -> True
[-1] -> False
[1, 1] -> True
[1, -1] -> True
[1, 1, 1, 1] -> True
[1, 1, 1, 1, 1] -> False
[1, -1, -1, 1, -1, 1, 1, -1] -> True
[1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1] -> False
[1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1] -> False
[1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1] -> True

5
O que significa "OVSF"?
NoOneIsHere

5
Fator de espalhamento variável ortogonal , que se refere à maneira como são usados e também a uma propriedade útil que possuem. Isso não parecia muito relevante, mas o link da Wikipedia explica tudo (vagamente).
Lynn

Respostas:


8

Geléia , 18 16 14 11 bytes

^2/Eam2µḊ¿Ṭ

Saídas [1](verdade) para códigos OVSF, [](falsy) caso contrário.

Experimente online!

fundo

Como a resposta MATL do @ LuisMendo e a resposta Python do @ xnor , esse envio verifica a matriz de entrada "de dentro para fora".

Todo par de elementos (sem sobreposição) de um código OVSF de comprimento dois ou superior é essencialmente uma cópia do primeiro par, com os mesmos sinais ou com os dois sinais trocados. Da mesma forma, cada 4-tupla (sem sobreposição) de elementos de um código OVSF de comprimento quatro ou superior é essencialmente uma cópia da primeira 4-tupla, com os mesmos sinais ou com os dois sinais trocados. O mesmo vale para 8 tuplas, 16 tuplas, etc., até o comprimento do código OVFS.

Uma maneira de verificar isso é verificar primeiro todos os pares quanto ao módulo de igualdade, e depois remover o segundo elemento de cada par (que agora é uma informação redundante). Se repetirmos esse processo mais uma vez, estamos essencialmente verificando todas as quatro tuplas. Na próxima iteração, comparamos 8 tuplas etc.

Finalmente, se todos os 2 k- tuples exigidos fossem iguais ao módulo e o array fosse reduzido a um singleton, é suficiente verificar se o elemento restante é 1 .

Como funciona

^2/Eam2µḊ¿Ṭ  Main link. Argument: A (array of 1's and -1's)

       µḊ¿   While dequeuing A (removing its first element) yields a non-empty
             array, execute the monadic chain to the left, updating A with the
             return value after each iteration.
^2/            Compute the bitwise XOR of each non-overlapping pair of elements of
               A. Note that 1 ^ 1 = 0 = -1 ^ -1 and 1 ^ -1 = -2 = -1 ^ 1.
               For an array of even length that consists of the same pairs modulo
               the sign, this returns either an array of 0's or an array of -2's.
               If the length is odd, it will also contain the last element, which
               is either a 1 or a -1.
   E           Test the elements of the result for equality. This yields 1 if the
               array consists solely of 0's or solely of -2's, 0 otherwise.
    a          Take the logical AND of the previous result and every element of A.
               This returns A if it passed the previous test, but replaces all of
               its elements with 0's otherwise.
     m2        Modulo 2; select every second element of A, starting with the first.
             At this point, the last return value can be:
               • [  ] if the input was empty
               • [ 1] if the input was a valid OVSF code
               • [-1] if the input was the negative of a valid OVSF code.
               • [ 0] in all other cases.
           Ṭ  Untruth; yield an array with 1's at the specified indices.
              Indexing is 1-based in Jelly, so [1] returns [1], the array with a 1
              at index 1. Since the indices -1 and 0 are non-canonical, the arrays
              [-1] and [0] are mapped to []. The empty array remains empty.

14

Mathematica, 52 47 45 bytes

A contagem de bytes assume a codificação CP-1252 e $CharacterEncodingdefinida como WindowsANSI(o padrão nas instalações do Windows).

±___=!(±1=1>0)
a__±b__/;a!==b!||{a}==-{b}:=±a

Isso define uma função variável PlusMinus, que pega a lista de entrada como uma lista simples de argumentos e retorna um booleano, por exemplo, PlusMinus[1, -1, -1, 1]True. É teoricamente também pode ser usado como um operador ±, mas esse operador só é sintaticamente válido em contextos unários e binários, de modo a convenção de chamada iria ficar estranho: ±##&[1,-1,-1,1]. Ele lançará vários avisos que podem ser ignorados.

Isso também emitirá alguns avisos que podem ser ignorados.

Não pode ser afastado para encurtar a pouco chato a!==b!||{a}==-{b}parte, mas eu não estou achando nada agora. Palavras-chave gostam SubsetQe MatrixRanksão simplesmente muito longas. : /

Explicação

A solução basicamente adia todas as coisas complicadas ao comparador de padrões do Mathematica e, portanto, é muito declarativa em estilo. Além de alguma golfitude na primeira linha, isso realmente apenas adiciona três definições diferentes para o operador ±:

±___=False;
±1=True;
a__±b__/;a!==b!||{a}==-{b}:=±a

As duas primeiras linhas foram encurtadas, aninhando as definições e expressando Truecomo 1>0.

Deveríamos desconstruir isso ainda mais para mostrar como isso realmente define uma função variadci PlusMinususando apenas notação de operador unário e binário. O problema é que todos os operadores são simplesmente açúcar sintático para expressões completas. No nosso caso ±corresponde a PlusMinus. O código a seguir é 100% equivalente:

PlusMinus[___]=False;
PlusMinus[1]=True;
PlusMinus[a__,b__]/;a!==b!||{a}==-{b}:=PlusMinus[a]

Usando sequências (como splats semelhantes em outras línguas) como operandos ±, podemos cobrir um número arbitrário de argumentos PlusMinus, mesmo que ±não seja utilizável com mais de dois argumentos. A razão fundamental é que o açúcar sintático é resolvido primeiro, antes que qualquer uma dessas seqüências seja expandida.

Para as definições:

A primeira definição é simplesmente um fallback ( ___corresponde a uma lista arbitrária de argumentos). Tudo o que não corresponder às definições mais específicas abaixo dará False.

A segunda definição é o caso base para o OVSF, a lista que contém apenas 1. Definimos que seja True.

Finalmente, a terceira definição se aplica apenas a listas que podem ser decompostas em X ++ Xou X ++ -Xe usa o resultado recursivamente para X. A definição está limitado a estas listas, assegurando que pode ser dividida em subsequências ae bcom a__±b__e, em seguida, fixar a condição ( /;) que seja {a}=={b}ou {a}==-{b}. Definir PlusMinusuma função variada dessa maneira estranha por meio de um operador economiza 5 bytes em comparação à definição de um operador unário ±nas listas.

Mas espere, tem mais. Estamos usando em a!==b!vez de {a}=={b}. Claramente, estamos fazendo isso porque são dois bytes mais curtos, mas a questão interessante é por que isso funciona. Como expliquei acima, todos os operadores são apenas açúcar sintático para alguma expressão com uma cabeça. {a}é List[a]. Mas aé uma sequência (como eu disse, mais ou menos como um splat em outras línguas), então, se aé 1,-1,1assim que chegamos List[1,-1,1]. Agora o postfix !é Factorial. Então aqui nós teríamos Factorial[1,-1,1]. Mas Factorialnão sabe o que fazer quando tem um número diferente de argumentos que um, então isso simplesmente permanece sem avaliação. ==realmente não se importa se a coisa de ambos os lados são listas, apenas compara as expressões e, se são iguais, forneceTrue(nesse caso, na verdade não dará Falsese não estiverem, mas os padrões não corresponderão se a condição retornar algo diferente de True). Portanto, a verificação de igualdade ainda funcionará se houver pelo menos dois elementos nas listas. E se houver apenas um? Se aé 1então a!é ainda 1. Se aé -1então a!ComplexInfinity. Agora, comparar 1a si mesmo ainda funciona bem, é claro. Mas ComplexInfinity == ComplexInfinitypermanece não avaliado e, apesar de tudo , não é verdadeiro a == -1 == b. Felizmente, isso não importa, porque a única situação em PlusMinus[-1, -1]que aparece é que não é um OVSF válido de qualquer maneira! (Se a condição retornar True, a chamada recursiva informaráFalseafinal de contas, não importa que a verificação não funcione.) Não podemos usar o mesmo truque {a}==-{b}porque, porque -não seria encadeado Factorial, apenas encadeava List.

O correspondente de padrões cuidará do resto e simplesmente encontrará a definição correta a ser aplicada.


9

Haskell, 57 bytes

q=length
f l=l==until((>=q l).q)(\s->s++map(*l!!q s)s)[1]

Dada a lista de entradas l, cresce um código OVSF siniciando [1]e concatenando repetidamente um sou outro -s, o que faz o primeiro elemento corresponder ao de l. Em seguida, verifica se o resultado está lno final. Isso é verificado quando o scomprimento for pelo menos igual a l.

Algumas estruturas recursivas alternativas também deram 57:

(s%i)l|length l<=i=s==l|j<-2*i=(s++map(*l!!i)s)%j$l
[1]%1

q=length
s%l|q s>=q l=s==l|r<-s++map(*l!!q s)s=r%l
([1]%)

q=length
g s l|q s<q l=g(s++map(*l!!q s)s)l|1>0=s==l
g[1]

6

MATLAB / oitava , 94 bytes

function a=f(r);n=nnz(r);m=log2(n);a=0;if fix(m)-m==0;for c=hadamard(n);a=a+all(r==c');end;end

Isso está usando uma nova abordagem: Os códigos de comprimento OVSF permitidos Naparecem na matriz de Walsh-log2(N) th , como são basicamente definidos pela mesma recursão:

As matrizes de Walsh são casos especiais das matrizes Hadamard de tamanho, N x Nse Nfor uma potência de duas. (Existem também matrizes Hadamard de outros tamanhos.) MATLAB e Octave têm uma variedade de funções internas que geram matrizes de teste para testar propriedades de algoritmos numéricos, entre elas está hadamard(). Felizmente, para poderes de duas hadamard()usinas do MATLAB, exatamente a construção das matrizes galesas.

Portanto, essa função primeiro verifica se o comprimento das entradas é uma potência de dois e, se for, verifica se é uma linha do tamanho correspondente da matriz galesa.

Experimente online!


5

Python, 64 bytes

f=lambda l:[]<l[1::2]==[x*l[1]for x in l[::2]]*f(l[::2])or[1]==l

Divide a lista em elementos indexados pares e indexados ímpares por meio de fatias. Verifica se os vetores de resultado são iguais ou negativos, multiplicando um pelo sinal forçado pelo seu primeiro elemento. Em seguida, faz a mesma verificação recursiva nos elementos indexados pares.

Para o caso base, se a verificação falhar, será rejeitada, a menos que a lista esteja [1]. A lista vazia também é especificamente rejeitada para evitar um loop infinito.

Uma estratégia diferente como a minha resposta Haskell fornece 66 bytes:

f=lambda l,i=1,s=[1]:l[i:]and f(l,i*2,s+[x*l[i]for x in s])or s==l

2

Haskell , 106 91 87 86 bytes

g n|n<1=[[1]]|m<-g(n-1)=foldl(\a b->[b++map(0-)b,b++b]++a)[]m++m
f l=elem l$g$length l

A função ggera a niteração das listas (de forma relativamente ineficiente, pois length $ g n == 3^n, no entanto, se excluirmos as duplicatas, obteríamos 2^n), fverifica se nossa lista está em alguma delas. Obrigado ao @Zgrab por algumas dicas!

Experimente online!


A execução dos dois últimos casos de teste não produziu uma saída para mim.
Oliver

@obarakon Sim, isso é porque gé muito ineficiente e produz uma tonelada de duplicatas. (Verifique a depuração seção, é provavelmente devido às limitações de tempo ou de memória.)
flawr

2

JavaScript (ES6), 130 93 87 85 83 bytes

f=a=>(b=a.slice(0,l=a.length/2),c=a.slice(l)+"",a==1||l&&b==c|b.map(i=>-i)==c&f(b))

Demo

f=a=>(b=a.slice(0,l=a.length/2),c=a.slice(l)+"",a==1||l&&b==c|b.map(i=>-i)==c&f(b)),[[],[1],[-1],[1,1],[1,-1],[1,1,1,1],[1,1,1,1,1],[1,-1,-1,1,-1,1,1,-1],[1,1,1,1,-1,-1,-1,-1,1,1,1,1],[1,1,1,1,-1,-1,-1,-1,1,1,1,1,1,1,1,1],[1,1,1,1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1]].map(a=>console.log(`[${a}] -> ${!!f(a)}`))


2

JavaScript (ES6), 85 61 bytes

a=>(l=a.length)&&!(l&l-1)&a.every((e,i)=>e==a[j=i&-i]*a[i-j])

Versão anterior que verificava os elementos para garantir que fossem 1ou -1:

a=>(l=a.length)&&!(l&l-1)&a.every((e,i)=>i?(j=i&-i)<i?e==a[j]*a[i-j]:e==1|e==-1:e==1)

Explicação:

  • O comprimento não pode ser zero
  • O comprimento deve ser uma potência de 2
  • O primeiro elemento deve ser 1
  • Elementos em posições com potência 2 devem ser 1 ou -1
  • Elementos em outras posições são o produto de todos os elementos nas posições correspondentes à máscara de bit, por exemplo a[22] == a[2] * a[4] * a[16]. Como a[20] == a[4] * a[16]já foi verificado, só a[22] == a[2] * a[20]precisa ser verificado.
  • A verificação acima fornece resultados degenerados por inão ter pelo menos dois bits definidos. No caso de zero bits definido, ele verifica se a[0] == a[0] * a[0], o que é falso a[0] == -1, enquanto no caso de um bit definido, verifica isso a[i] == a[0] * a[i].

Você pode alterar (l=a.length)&&!(l&l-1)para (l=a.length)&-l==lsalvar 4 bytes
Patrick Roberts

@PatrickRoberts Isso não é verdade l==0?
Neil

Oh, você está certo. Bem então (l=a.length)&&l&-l==l? para salvar 1 byte ...
Patrick Roberts

Na verdade, deixa pra lá, sua função falha no caso [1,1,1,1,-1,-1,-1,-1,1,1,1,1,1,1,1,1]mesmo sem minhas sugestões.
Patrick Roberts

@PatrickRoberts l&-l==lnão funciona porque ==tem maior precedência que &. E o caso de teste não funciona por causa de um erro de digitação que me custará um byte para corrigir.
Neil

2

MATL , 21 20 bytes

`2eZ}yy=&=tn1>hh]1X=

Experimente online! Ou verifique todos os casos de teste .

Como funciona

O código divide a matriz em duas partes iguais: a primeira com as entradas indexadas ímpares, a segunda com as entradas indexadas pares. As duas peças são forçadas a ter o mesmo comprimento, com um preenchimento zero no segundo, se necessário. Então o código verifica se

  1. As entradas correspondentes das duas peças são todas iguais ou todas diferentes;
  2. Nenhuma entrada na segunda peça é zero;
  3. O comprimento das peças excede 1.

Se essas três condições forem atendidas, o processo será aplicado novamente na primeira peça. Se o loop for encerrado porque o comprimento já era 1, a entrada é um código OFSV. Senão não é.

A condição 1 iterada é uma versão equivalente da propriedade definidora dos códigos OVSF. Para uma matriz de comprimento 8, a abordagem direta seria verificar se as entradas 1,2,3,4 são todas iguais ou diferentes das entradas 5,6,7,8, respectivamente (esta é a propriedade de definição). Mas podemos verificar de forma equivalente que as entradas 1,3,5,7 são todas iguais ou diferentes das entradas 2,4,6,8, respectivamente; e isso acaba usando menos bytes.

A condição 2 garante que o comprimento da entrada seja uma potência de 2: se não for, um zero de preenchimento será introduzido em algum estágio.

`        % Do...while loop
  2e     %   Reshape as a two-row matrix, with a padding zero if needed
         %   Row 1 contains the original odd-indexed entries, row 2 the
         %   even-indexed
  Z}     %   Split matrix into two vectors, one corresponding to each row
  yy     %   Duplicate those two vectors
  =      %   Check if corresponding entries are equal or not
  &=     %   Matrix of all pairwise comparisons. This will give a matrix
         %   filled with ones if and only if the previous check gave all
         %   true or all false (condition 1)
  tn1>   %   Duplicate and push true if size exceeds 1, or false otherwise
         %   (condition 3)
  hh     %   Concatenate condition 1, condition 3, and the original copy of
         %   the second piece (condition 2). The resulting vector is truthy
         %   if and only if it doesn't contain any zero
]        % End
1X=      % True if top of the stack is a single 1, false otherwise

2

Haskell, 66 bytes

Yay, listas infinitas!

o=[1]:(o>>= \x->[x++map(0-)x,x++x])
f l=l`elem`take(2*2^length l)o

Versões alternativas:

o=[1]:(o<**>map(>>=flip(++))[map(0-),id])
f=Data.List.Ordered.hasBy(comparing length)o

Obrigado pelo (0-)truque, eu estava preso com negateou((-1)*)
Bergi

1

APL, 46 bytes

{0::0⋄⍵≡,1:1⋄⍬≡⍵:0⋄(∇Z↑⍵)∧(∇Y)∨∇-Y←⍵↓⍨Z←.5×⍴⍵}

Bastante simples:

  • Casos básicos:
    • 0::0: se ocorrer um erro, retorne 0
    • ⍵≡,1:1: se a entrada for exatamente [1], retorne 1
    • ⍬≡⍵:0: se a entrada for a lista vazia, retorne 0
  • Caso recursivo:
    • Z←.5×⍴⍵: Zé metade do comprimento da entrada
    • Y←⍵↓⍨Z: Yé a última metade da entrada (isso falha se ⍴⍵for desigual, acionando o manipulador de exceções)
    • (∇Y)∨∇-Y: a última metade da lista ou a negação da última metade da lista devem ser um código OVSF
    • (∇Z↑⍵)∧: e a primeira metade da lista deve ser um código OVSF.

1
Não acho que seja suficiente verificar o código da OVSF para o segundo semestre; deve ser igual à primeira metade ou a sua negação.
Zgarb

1
eles dizem BASIC é um definhar alto nível e APL é um alto nível de angústia: ')
cat

eles dizem BASIC é um definhar alto nível e APL é um alto nível de angústia: ')
cat

1

Haskell, 69 68 bytes

g x=any(elem x)$scanr(\_->concat.mapM(\y->[y++y,y++map(0-)y]))[[1]]x

Exemplo de uso: g [-1,1]-> False.

Ainda mais ineficiente do que a resposta de @ flawr . Leva muito tempo e memória para 4 listas de elementos. Para ver que a lista de códigos OVSF (com muitas duplicatas) é realmente criada, tente:

take 10 $ c $ scanr(\_->concat.mapM(\y->[y++y,y++map(0-)y]))[[1]] [1..4]

que retorna

[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
 [1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1],
 [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
 [1,-1,-1,1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,1],
 [1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,1,1,-1,-1],
 [1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1],
 [1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,1,1,-1,-1],
 [1,-1,-1,1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,1],
 [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
 [1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1]]

ou seja, a lista começa com todas as 16 listas de elementos (4 vezes concatenadas, por causa de [1..4]), continua com todas as 8 listas de elementos e assim sucessivamente até terminar com [1].

Edit: @xnor salvou um byte. Obrigado!


Ah, eu esqueci totalmente scanr!
precisa saber é

Eu acho que você pode cortar um byte fazendo em any(elem x)vez de elem x$ce não definindo c.
Xnor


0

JavaScript (ES6), 80

f=(l,k=[1])=>l+l==k+k||l[k.length]&&f(l,k.concat(k))|f(l,k.concat(k.map(v=>-v)))

Cria recursivamente e verifica cada lista até o comprimento da lista de entrada, começando com [1].

O valor de retorno é JS verdade ou falsey, especificamente 1ou truese válido, 0ou falseou undefinedse não for válido.

Teste

f=(l,k=[1])=>l+l==k+k||l[k.length]&&f(l,k.concat(k))|f(l,k.concat(k.map(v=>-v)))

test=`[] -> False
[1] -> True
[-1] -> False
[1, 1] -> True
[1, -1] -> True
[1, 1, 1, 1] -> True
[1, 1, 1, 1, 1] -> False
[1, -1, -1, 1, -1, 1, 1, -1] -> True
[1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1] -> False
[1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1] -> False
[1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1] -> True`
.split('\n')

test.forEach(r=>{
  input = r.match(/-?1/g)||[]
  check = r.slice(-4) == 'True'
  result = f(input)
  console.log(result, check, '['+input+']')
})


0

Clojure, 118 bytes

(defn f[C](or(=(count C)1)(let[l(/(count C)2)[a b](split-at l C)](and(> l 0)(=(count b)l)(apply =(map * a b))(f a)))))

Entrada de racha cem duas metades ae be verifica se os seus produtos elemento-wise são todos idênticos. Nesse caso, verifica se a primeira metade é uma sequência válida.

Este tem 142 bytes, mas achei mais interessante:

#((set(nth(iterate(fn[I](mapcat(fn[i][(concat i i)(concat i(map - i))])I))[[1][-1]])(loop[l(count %)i 0](if(< l 2)i(recur(/ l 2)(inc i))))))%)

loopcalcula log_2o comprimento da entrada, iterategera sequências de muitas iterações com base na definição. Isso retorna o argumento de entrada se for uma sequência válida ou nilnão.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.