¿Es un código OVSF?

27

Dada una lista de 1s y -1s, determinar si es o no es un válido código OVSF (mediante la salida de un Truthy o el valor Falsey-).

Los códigos OVSF se definen de la siguiente manera:

  • [1] es un código OVSF

  • Si Xes un código OVSF, entonces X ++ Xy X ++ -Xambos son códigos OVSF.

    Aquí ++está la concatenación de listas y -niega todos los elementos de la lista.

  • Ninguna otra lista son códigos OVSF válidos.

Puede suponer que la lista de entrada contiene solo -1y 1, pero debe manejar la lista vacía correctamente, así como las listas cuya longitud no es una potencia de 2.

El código más corto (en bytes) gana.

Casos de prueba

[] -> 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
Lynn
fuente
55
¿Qué significa "OVSF"?
NoOneIsHere
55
Factor de dispersión variable ortogonal , que se refiere a la forma en que se usan y también a una propiedad útil que tienen. Esto no parecía muy relevante, pero el enlace de Wikipedia lo explica todo (vagamente).
Lynn

Respuestas:

8

Jalea , 18 16 14 11 bytes

^2/Eam2µḊ¿Ṭ

Salidas [1](verdadero) para códigos OVSF, [](falso) de lo contrario.

Pruébalo en línea!

Fondo

Al igual que @ respuesta MAT de LuisMendo y @ de XNOR respuesta Python , esta presentación verifica la matriz de entrada "del cabo dentro".

Cada par (no superpuesto) de elementos de un código OVSF de longitud dos o superior es esencialmente una copia del primer par, ya sea con los mismos signos o con ambos signos intercambiados. Del mismo modo, cada 4 tuplas (no superpuestas) de elementos de un código OVSF de longitud cuatro o superior es esencialmente una copia de las primeras 4 tuplas, ya sea con los mismos signos o con ambos signos intercambiados. Lo mismo es cierto para 8 tuplas, 16 tuplas, etc., hasta la longitud del código OVFS.

Una forma de verificar esto es verificar primero todos los pares para el módulo de igualdad del signo, luego eliminar el segundo elemento de cada par (que ahora es información redundante). Si repetimos este proceso una vez más, esencialmente estamos verificando las 4 tuplas. En la próxima iteración, estamos comparando 8-tuplas, etc.

Finalmente, si todas las 2 k- tuplas requeridas fueran iguales módulo el signo y la matriz se ha reducido a un singleton, es suficiente verificar si el elemento restante es un 1 .

Cómo 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.
Dennis
fuente
14

Mathematica, 52 47 45 bytes

El recuento de bytes supone la codificación CP-1252 y se $CharacterEncodingestablece en WindowsANSI(el valor predeterminado en las instalaciones de Windows).

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

Esto define una función variadic PlusMinus, que toma la lista de entrada como una lista plana de argumentos y devuelve un valor booleano, por ejemplo, PlusMinus[1, -1, -1, 1]da True. Es teóricamente también puede utilizarse como un operador ±, pero que el operador sólo es válido sintácticamente en contextos unarios y binarios, por lo que la convención de llamada obtendría raro: ±##&[1,-1,-1,1]. Lanzará un montón de advertencias que pueden ignorarse.

Esto también arrojará algunas advertencias que pueden ignorarse.

No podría estar lejos de acortar el algo molesto a!==b!||{a}==-{b}parte, pero no estoy encontrando nada en este momento. Palabras clave como SubsetQy MatrixRankson simplemente demasiado largas. : /

Explicación

La solución básicamente difiere todas las cosas difíciles para el patrón de Mathematica y, por lo tanto, tiene un estilo muy declarativo. Además de cierta golfitud en la primera línea, esto realmente agrega tres definiciones diferentes para el operador ±:

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

Las dos primeras filas se acortaron anidando las definiciones y expresando Truecomo 1>0.

Deberíamos deconstruir esto aún más para mostrar cómo esto realmente define una función variadci PlusMinususando solo la notación de operador unario y binario. El problema es que todos los operadores son simplemente azúcar sintáctica para expresiones completas. En nuestro caso ±corresponde a PlusMinus. El siguiente código es 100% equivalente:

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

Mediante el uso de secuencias (más o menos como símbolos en otros idiomas) como operandos ±, podemos cubrir una cantidad arbitraria de argumentos para PlusMinus, aunque ±no sea utilizable con más de dos argumentos. La razón fundamental es que el azúcar sintáctico se resuelve primero, antes de que cualquiera de estas secuencias se expanda.

A las definiciones:

La primera definición es simplemente una alternativa ( ___coincide con una lista arbitraria de argumentos). Cualquier cosa que no coincida con las definiciones más específicas a continuación dará False.

La segunda definición es el caso base para el OVSF, la lista contiene solo 1. Definimos esto para ser True.

Finalmente, la tercera definición se aplica solo a las listas que se pueden descomponer en X ++ Xo X ++ -X, y utiliza recursivamente el resultado para X. La definición se limita a estas listas asegurándose de que se puede dividir en subsecuencias ay bcon a__±b__y luego unir la condición ( /;) que, o bien {a}=={b}o {a}==-{b}. La definición PlusMinuscomo una función variada de esta forma extraña a través de un operador ahorra la friolera de 5 bytes sobre la definición de un operador unario ±en las listas.

Pero espera hay mas. Estamos usando en a!==b!lugar de {a}=={b}. Claramente, estamos haciendo esto porque es dos bytes más corto, pero la pregunta interesante es por qué funciona. Como he explicado anteriormente, todos los operadores son solo azúcar sintáctico para alguna expresión con cabeza. {a}es List[a]. Pero aes una secuencia (como dije, algo así como un splat en otros idiomas) así que si aes 1,-1,1así, obtenemos List[1,-1,1]. Ahora postfix !es Factorial. Así que aquí, llegaríamos Factorial[1,-1,1]. Pero Factorialno sabe qué hacer cuando tiene un número diferente de argumentos que uno, por lo que esto simplemente permanece sin evaluar. ==realmente no le importa si las cosas en ambos lados son listas, solo compara las expresiones, y si son iguales daTrue(en este caso, no dará realmente Falsesi no lo son, pero los patrones no coinciden si la condición devuelve algo más que True). Eso significa que la verificación de igualdad aún funciona si hay al menos dos elementos en las listas. ¿Qué pasa si solo hay uno? Si aes, 1entonces a!está quieto 1. Si aes -1entonces a!da ComplexInfinity. Ahora, compararse 1a sí mismo todavía funciona bien, por supuesto. Pero ComplexInfinity == ComplexInfinitypermanece sin evaluar, y no da verdad a pesar de todo a == -1 == b. Afortunadamente, esto no importa, porque la única situación en la que esto aparece es PlusMinus[-1, -1]que no es un OVSF válido de todos modos. (Si la condición volviera True, la llamada recursiva informaríaFalsedespués de todo, así que no importa que el cheque no funcione). No podemos usar el mismo truco {a}==-{b}porque -no Factorialse enhebra, solo se enhebra List.

El emparejador de patrones se encargará del resto y simplemente encontrará la definición correcta para aplicar.

Martin Ender
fuente
9

Haskell, 57 bytes

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

Dada la lista de entrada l, crece un código OVSF scomenzando desde [1]y concatenando repetidamente cualquiera so -s, lo que haga que el primer elemento coincida con el de l. Luego, verifica que el resultado esté lal final. Esto se verifica una vez que stiene una longitud de al menos la de l.

Algunas estructuras recursivas alternativas también dieron 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]
xnor
fuente
6

MATLAB / Octave , 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

Esto está utilizando un nuevo enfoque: los códigos de longitud OVSF permitidos Naparecen en la log2(N)-math Walsh-matrix , ya que están básicamente definidos por la misma recursividad:

Las matrices de Walsh son casos especiales de las matrices de tamaño HadamardN x N si Nes una potencia de dos. (También hay matrices Hadamard de otros tamaños). MATLAB y Octave tienen una variedad de funciones integradas que generan matrices de prueba para probar las propiedades de algoritmos numéricos, entre ellas hadamard(). Afortunadamente para los poderes de dos MATLAB, hadamard()usex exactamente la construcción de las matrices galesas.

Entonces, esta función primero verifica si la longitud de las entradas es una potencia de dos, y si lo es, verifica si es una fila del tamaño correspondiente de la matriz de Gales.

Pruébalo en línea!

falla
fuente
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 la lista en elementos indexados pares y elementos indexados impares a través de sectores. Comprueba si los vectores de resultado son iguales o negativos multiplicando uno por el signo forzado por su primer elemento. Luego, realiza la misma verificación recursiva en los elementos indexados pares.

Para el caso base, si la verificación falla, se rechaza a menos que la lista lo sea [1]. La lista vacía también se rechaza específicamente para evitar un bucle infinito.

Una estrategia diferente como mi respuesta de Haskell da 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
xnor
fuente
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

La función ggenera la niteración de listas (relativamente ineficiente, ya que length $ g n == 3^n, sin embargo, si eliminamos los duplicados, obtendríamos 2^n), fverifica si nuestra lista está en alguno de ellos. ¡Gracias a @Zgrab por algunas pistas!

Pruébalo en línea!

falla
fuente
Ejecutar los últimos 2 casos de prueba no me dio un resultado.
Oliver
@obarakon Sí, eso es porque ges muy ineficiente y produce una tonelada de duplicados. (Verifique la sección de depuración , probablemente se deba a limitaciones de tiempo o memoria.)
error
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))

Manifestación

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

Patrick Roberts
fuente
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])

Versión anterior que verificó elementos para asegurarse de que eran 1o -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)

Explicación:

  • La longitud no puede ser cero.
  • La longitud debe ser una potencia de 2
  • El primer elemento debe ser 1
  • Los elementos en posiciones que tienen una potencia de 2 deben ser 1 o -1
  • Los elementos en otras posiciones son el producto de todos los elementos en las posiciones correspondientes a la máscara de bits, por ejemplo a[22] == a[2] * a[4] * a[16]. Como a[20] == a[4] * a[16]ya se ha verificado, solo a[22] == a[2] * a[20]es necesario verificarlo.
  • La comprobación anterior proporciona resultados degenerados por ino tener al menos dos bits establecidos. En el caso del conjunto de bits cero, verifica eso a[0] == a[0] * a[0], lo cual es falso a[0] == -1, mientras que en el caso de un conjunto de bits, lo verifica a[i] == a[0] * a[i].
Neil
fuente
Puede cambiar (l=a.length)&&!(l&l-1)para (l=a.length)&-l==lguardar 4 bytes
Patrick Roberts
@PatrickRoberts ¿No es cierto eso l==0?
Neil
Oh tienes razon Bien entonces (l=a.length)&&l&-l==l? guardar 1 byte ...
Patrick Roberts
En realidad, no importa, su función falla para el caso, [1,1,1,1,-1,-1,-1,-1,1,1,1,1,1,1,1,1]incluso sin mis sugerencias.
Patrick Roberts el
@PatrickRoberts l&-l==lno funciona porque ==tiene mayor prioridad que &. Y el caso de prueba no funciona debido a un error tipográfico que me costará un byte solucionar.
Neil
2

MATL , 21 20 bytes

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

Pruébalo en línea! O verificar todos los casos de prueba .

Cómo funciona

El código divide la matriz en dos partes de igual longitud: la primera con las entradas indexadas impares, la segunda con las entradas indexadas pares. Las dos piezas están obligadas a tener la misma longitud, con un relleno cero en el segundo si es necesario. Entonces el código verifica que

  1. Las entradas correspondientes de las dos piezas son todas iguales o todas diferentes;
  2. Ninguna entrada en la segunda pieza es cero;
  3. La longitud de las piezas supera 1.

Si se cumplen estas tres condiciones, el proceso se aplica nuevamente en la primera pieza. Si se sale del bucle porque la longitud ya era 1, la entrada es un código OFSV. De lo contrario no lo es.

La condición 1 iterada es una versión equivalente de la propiedad definitoria de los códigos OVSF. Para una matriz de, digamos, longitud 8, el enfoque directo sería verificar que las entradas 1, 2, 3, 4 sean todas iguales o diferentes a las entradas 5, 6, 7, 8 respectivamente (esta es la propiedad que define). Pero podemos comprobar de manera equivalente que las entradas 1,3,5,7 son todas iguales o diferentes a las entradas 2,4,6,8 respectivamente; y esto resulta usar menos bytes.

La condición 2 asegura que la longitud de entrada sea una potencia de 2: si no es así, se introducirá un relleno cero en algún momento.

`        % 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
Luis Mendo
fuente
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

Versiones alternativas:

o=[1]:(o<**>map(>>=flip(++))[map(0-),id])
f=Data.List.Ordered.hasBy(comparing length)o
Bergi
fuente
Gracias por el (0-)truco, estaba atrapado con negateo((-1)*)
Bergi
1

APL, 46 bytes

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

Bastante sencillo:

  • Casos base:
    • 0::0: si se produce un error, devuelve 0
    • ⍵≡,1:1: si la entrada es exactamente [1], devuelve 1
    • ⍬≡⍵:0: si la entrada es la lista vacía, devuelve 0
  • Caso recursivo:
    • Z←.5×⍴⍵: Zes la mitad de la longitud de la entrada
    • Y←⍵↓⍨Z: Yes la última mitad de la entrada (esto falla si ⍴⍵es desigual, activando el controlador de excepciones)
    • (∇Y)∨∇-Y: la última mitad de la lista, o la negación de la última mitad de la lista, debe ser un código OVSF
    • (∇Z↑⍵)∧: y la primera mitad de la lista debe ser un código OVSF.
marinus
fuente
1
No creo que sea suficiente verificar la codificación OVSF para la segunda mitad; debe ser igual a la primera mitad o su negación.
Zgarb
1
dicen que BASIC es un lenguaje de alto nivel y APL es un alto nivel de angustia: ')
gato
dicen que BASIC es un lenguaje de alto nivel y APL es un alto nivel de angustia: ')
gato
1

Haskell, 69 68 bytes

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

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

Aún más ineficiente que la respuesta de @ flawr . Se necesita demasiado tiempo y memoria para las listas de 4 elementos. Para ver que realmente se crea la lista de códigos OVSF (con muchos duplicados), intente:

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

que vuelve

[[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]]

es decir, la lista comienza con las 16 listas de elementos (4 veces concatenadas, debido a [1..4]), continúa con las 8 listas de elementos y así sucesivamente hasta que termina con [1].

Editar: @xnor guardó un byte. ¡Gracias!

nimi
fuente
¡Ah, me olvidé por completo scanr!
flawr
Creo que puede cortar un byte haciendo en any(elem x)lugar de elem x$cy no definiendo 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)))

Construye recursivamente y verifica cada lista hasta la longitud de la lista de entrada, comenzando con [1].

El valor de retorno es JS Truthy o Falsey-, en concreto 1, o truesi es válido, 0o false, o undefinedsi no es válido.

Prueba

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+']')
})

edc65
fuente
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)))))

Divide la entrada cen dos mitades ay bcomprueba si sus productos con elementos son todos idénticos. Si es así, verifica que la primera mitad sea una secuencia válida.

Este es de 142 bytes pero lo encontré más interesante:

#((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_2la longitud de la entrada, iterategenera secuencias de tantas iteraciones basadas en la definición. Esto devuelve el argumento de entrada si es una secuencia válida y de lo nilcontrario.

NikoNyrh
fuente