Decodificar um mapa de calor

32

Heatmaps

Considere uma sala retangular, em cujo teto temos uma câmera térmica apontando para baixo. Na sala, há um certo número de fontes de calor de intensidade 1-9, sendo a temperatura de fundo 0. O calor se dissipa de cada fonte, diminuindo uma unidade por etapa (não diagonal). Por exemplo, a 20x10sala

...........1........
....................
...8................
..5...............2.
....................
.1..................
................1...
.................65.
....................
............2.......

contém 9 fontes de calor, e o gradiente de temperatura mostrado pela câmera térmica é

34565432100100000000
45676543210000000000
56787654321000000110
45676543210000001221
34565432100000012321
23454321000000123432
12343210000001234543
01232100000012345654
00121000000011234543
00010000000121123432

Em forma gráfica, isso pode se parecer com:

mapa de calor de 9 fontes

A partir do gradiente, podemos inferir as posições e intensidades de algumas fontes de calor, mas não todas. Por exemplo, todos os 9s sempre podem ser inferidos, uma vez que possuem a temperatura máxima, e o mesmo pode 8ocorrer neste caso, pois produz um máximo local no gradiente. A 2borda próxima à direita também pode ser inferida, mesmo que não esteja no máximo local, pois não possui outra 2como vizinha. Os 5s, por outro lado, não são inferidos, pois seu calor também pode ser produzido pelas fontes mais intensas próximas a eles. Os 0s são conhecidos por conter nenhuma fonte de calor, mas todas as outras peças podem potencialmente conter um. Vamos denotar os azulejos incertos por hífens-, determinadas fontes de calor pelos dígitos correspondentes e certo espaço vazio por períodos .:

---------..1........
----------..........
---8-------......--.
----------......--2-
---------......-----
--------......------
-------......-------
.-----......-----6--
..---.......--------
...-.......-2-------

Sua tarefa será produzir esse padrão inferido a partir do gradiente de temperatura.

Regras

Você recebe a entrada como uma sequência delimitada por novas linhas ou tubos verticais |, o que for mais conveniente, e a saída deve ter a mesma forma. Pode haver um delimitador à direita na entrada e / ou saída, mas nenhum precedente. O tamanho da entrada pode variar, mas sua largura e altura são sempre pelo menos 4. Ambas as funções e programas completos são aceitáveis. A contagem de bytes mais baixa vence e as brechas padrão são proibidas.

Casos de teste adicionais

Entrada:

898778765432100
787667654321100
677656543211210
678765432112321
567654321123210

que se parece com isso na forma gráfica:

caso de teste 1

Saída:

-9---8-------..
-------------..
--------------.
--8---------3--
-----------3--.

Entrada:

7898
8787
7676
6565

Saída:

--9-
8---
----
----

Entrada:

00001
00000
00000
10000

Saída:

....1
.....
.....
1....
Zgarb
fonte
11
Você se importa se eu adicionar 2 gráficos de mapa de calor à sua pergunta, se você acha que eles agregam valor? Eles são apenas um experimento de 2 minutos.
Logic Knight
@CarpetPython Claro, vá em frente. Eles parecem muito legais comigo. Você também pode adicionar uma "Cortesia de CarpetPython" para se dar o crédito. ;)
Zgarb
2
Feito. Não é necessário crédito, mas achei que seria rude não perguntar antes de editar.
Logic Knight
Por que não permitir a entrada como uma matriz bidimensional em vez de uma string?
feersum
Os métodos @feersum geralmente são consistentes.
Optimizer

Respostas:

10

CJam, 73 69 62 55 bytes

UPDATE : Novo algoritmo. Mais curto e mais espaço para melhorias

qN/5ff*{{[{_@_@<{I'0t}*\}*]W%}%z}4fI{):X-'-X~X'.??}f%N*

Como funciona

A lógica é semelhante ao algoritmo abaixo, mas aqui não estou verificando todos os quatro vizinhos em uma única iteração. Em vez disso, uso uma abordagem menor para percorrer todas as linhas e colunas nas duas direções. Aqui estão as etapas envolvidas:

  • Converta cada caractere em conjuntos de 5. Os 4 primeiros serão modificados para saber se são maiores que a célula adjacente na linha durante a iteração. O último é para fins de comparação.
  • Iterar em cada linha e reduzir em cada linha. Ao reduzir, tenho duas sequências de 5 caracteres. Eu sei que tipo de iteração é [0 para linhas normais, 1 coluna invertida, 2 para linhas invertidas e 3 para colunas normais] Eu atualizo o i- ésimo caractere na primeira cadeia de 5 caracteres e o faço 0 se for menor que a segunda .
  • Após todas as 4 iterações, se todos os 5 caracteres forem iguais e diferentes de zero, esse é o máximo local. Mapeio todas as sequências de 5 caracteres e as converto em um único dígito .ou -.

Aqui está um exemplo executado em uma pequena entrada:

7898
8787
7676
6565

Após o primeiro passo:

["77777" "88888" "99999" "88888"
 "88888" "77777" "88888" "77777"
 "77777" "66666" "77777" "66666"
 "66666" "55555" "66666" "55555"]

Após o segundo passo:

["00777" "08888" "99999" "88088"
 "88888" "07007" "88808" "77007"
 "77707" "06006" "77707" "66006"
 "66606" "05005" "66606" "55005"]

Após o último mapeamento para um caractere, saída final:

--9-
8---
----
----

Código Explicação :

qN/5ff*                         "Split the input on new line and convert each character";
                                "to string of 5 of those characters.";
{{[{             }*]W%}%z}4fI   "This code block runs 4 times. In each iteration, it";
                                "maps over each row/column and then for each of them,";
                                "It reduce over all elements of the row/column";
                                "Using combination of W% and z ensures that both rows and";
                                "columns are covered and in both directions while reducing";
    _@_@                        "Take a copy of last two elements while reducing over";
        <                       "If the last element is bigger than second last:";
         {I'0t}*\               "Convert the Ith character of the 5 char string of"
                                "second last element to 0";
                                "We don't have to compare Ith character of last two 5 char";
                                "string as the smaller one will be having more leading";
                                "0 anyways. This saves 4 bytes while comparing elements";
{):X-'-X~X'.??}f%N*             "This part of code converts the 5 char back to single char";
 ):X                            "Remove the last character and store in X. This last char";
                                "was not touched in the prev. loop, so is the original char";
    -                           "Subtract X from remaining 4 char. If string is not empty";
                                "then it means that it was not all same characters";
                                "In other words, this character was smaller then neighbors";
     '-      ?                  "If non-empty, then replace with - else ...";
       X~X'.?                   "if int(X) is zero, put . else put X";
               f%N*             "The mapping code block was run for each row and then";
                                "The rows are joined by newline.";

Experimente aqui


Abordagem mais antiga

qN/~_,):L0s*]0s*:Q_,{QI=:A[W1LL~)]If+Qf=$W=<'-A?A~\'.?I\t}fIL/W<Wf<N*

Como funciona

A lógica é simples, percorre a grade e verifica se o valor atual é maior ou igual aos quatro vizinhos restantes - cima, baixo, esquerda e direita. Em seguida, transforme o valor atual com base na regra acima e, se o valor for igual a 0, torne-o "." .

Código Explicação

qN/~_,):L0s*]0s*:Q         "This part of code pads the grid with 0s";
qN/~                       "Read the input, split on new lines and unwrap the arrays";
    _,):L                  "Copy the last row, taken length, increment and store in L";
         0s*               "Get L length 0 string";
            ]0s*           "Wrap everything in an array and join the rows by 0";
                :Q         "Store this final single string in Q";

_,{        ...      }fI    "Copy Q and take length. For I in 0..length, execute block";
   QI=:A                   "Get the I'th element from Q and store in A";
   [WiLL~)]If+             "This creates indexes of all 4 neighboring cells to the Ith cell";
              Qf=          "Get all 4 values on the above 4 indexes";
                 $W=       "Sort and get the maximum value";
<'-A?                      "If the current value is not the largest, convert it to -";
     A~\'.?                "If current value is 0, convert it to .";
           I\t             "Update the current value back in the string";
{ ... }fIL/                "After the loop, split the resulting string into chunks of L";
           W<Wf<           "Remove last row and last column";
                N*         "Join by new line and auto print";

Experimente online aqui

Optimizer
fonte
5
Devo dizer que raramente ouço "muito tempo" ao descrever o código CJam.
18715 Alex A.
6

JavaScript (ES6) 99

F=h=>[...h].map((c,i)=>[o=~h.search('\n'),-o,1,-1].some(d=>h[d+i]>c)&c>0?'-':c=='0'?'.':c).join('')

Teste no console Firefox / FireBug

console.log(F('\
34565432100100000000\n\
45676543210000000000\n\
56787654321000000110\n\
45676543210000001221\n\
34565432100000012321\n\
23454321000000123432\n\
12343210000001234543\n\
01232100000012345654\n\
00121000000011234543\n\
00010000000121123432\n'),'\n\n',
F('\
898778765432100\n\
787667654321100\n\
677656543211210\n\
678765432112321\n\
567654321123210\n'), '\n\n',
F('7898\n8787\n7676\n6565\n'))

Saída

---------..1........
----------..........
---8-------......--.
----------......--2-
---------......-----
--------......------
-------......-------
.-----......-----6--
..---.......--------
...-.......-2-------


-9---8-------..
-------------..
--------------.
--8---------3--
-----------3--.


--9-
8---
----
----
edc65
fonte
4

Python 2: 154 byte

b=input()
l=b.index('\n')+1
print''.join(('\n.'+('-'+v)[all([v>=b[j]for j in i-l,i-1,i+l,i+1if 0<=j<len(b)])])[('\n0'+v).index(v)]for i,v in enumerate(b))

Entrada tem que ser da forma "00001\n00000\n00000\n10000".

A conversão de uma string em uma matriz 2D é bastante demorada no Python. Então, eu mantenho o formato da string original. Eu enumero sobre a entrada, ié o índice, vé o caractere (Finalmente enumere os bytes salvos em uma solução de golfe !!). Para cada par (i,v), calculo o caractere de saída correto e os uno. Como escolho o caractere de saída correto? Se v == '\n', o caractere de saída é \n, então v == '0', o caractere de saída '.'. Caso contrário, testo os 4 vizinhos de v, que são b[i-b.index('\n')-1](acima), b[i-1](esquerda, b[i+1](direita) e b[i+b.index('\n')+1](abaixo), se estiverem, <= ve escolho o caractere '-'ouv. Aqui estou comparando caracteres e não os números, mas funciona muito bem, porque os valores ascii estão na ordem correta. Também não há problemas, se b[i-1]ou b[i+1]igualar '\n', porque ord('\n') = 10.

Pitão: 61 58

JhxQbVQK@QN~k@++b\.?\-f&&gT0<TlQ<K@QT[tNhN-NJ+NJ)Kx+b\0K)k

Mais ou menos uma tradução do script Python. Muito feio ;-)

Experimente on-line: Compilador / Executor Pyth Mesmo formato de entrada que a solução Python.

JhxQb      Q = input()
  xQb      Q.index('\n')
 h         +1
J          store in J

VQK@QN~k.....)k   k is initialized as empty string
VQ           )    for N in [0, 1, 2, ..., len(Q)-1]:
  K@QN                K = Q[n]
      ~k              k += ... (a char, computed in the next paragraph)
             )    end for
              k   print k

@...x+b\0K   ... is a char of len 3 (is constructed below)
     +b\0    the string "\n0"
    x    K   find Q[d] in this string and return index, if not found -1
@...         lookup in string at the computed position (this is done mod 3 automatically!)

++b\.?\-f&&gT0<TlQ<K@QT[tNhN-NJ+NJ)K   not to the string
                       [tNhN-NJ+NJ)    the list [d-1, d+1, d-J, d+j]
        f                              filter the list for indices T which
           gT0                            T >= 0
          &                               and
              <TlQ                        T < len(Q)
         &                                and
                  <K@QT                   Q[d] < Q[T]
     ?\-                           K   use "-" if len(filter) > 0 else Q[d]
                                       this creates the third char
++b\.                                  "\n" + "." + third char
Jakube
fonte
4

Perl, 77, 75, 72 70

Truques de correspondência de regex 2D padrão.

#!perl -p0
/
/;$x="(.{@-})?";y/0/./while s/$.$x\K$"|$"(?=$x$.)/-/s||($"=$.++)<9

Exemplo:

$ perl heat.pl <in.txt
---------..1........
----------..........
---8-------......--.
----------......--2-
---------......-----
--------......------
-------......-------
.-----......-----6--
..---.......--------
...-.......-2-------

Experimente aqui

nutki
fonte
3

Java, 307 , 304 , 303 , 299 298

Este é certamente um desafio "perfeito" para alguns codegolf Java :)

class M{public static void main(String[]a){int c=a[0].indexOf('|'),i=c,d,v;char[]r=a[0].replace("|","").toCharArray(),m=new char[(v=r.length+c)+c];for(;i<v;){m[i]=r[i++-c];}for(i=c;i<v;i++){a[0]=i%c<1?"\n":"";d=m[i];System.out.print(a[0]+(d<49?'.':m[i-c]>d|m[i+c]>d|m[i-1]>d|m[i+1]>d?'-':m[i]));}}}

Entrada (método '|' pipe):

34565432100100000000|45676543210000000000|56787654321000000110|45676543210000001221|34565432100000012321|23454321000000123432|12343210000001234543|01232100000012345654|00121000000011234543|00010000000121123432

Saída:

---------..1........
----------..........
---8-------......--.
----------......--2-
---------......-----
--------......------
-------......-------
.-----......-----6--
..---.......--------
...-.......-2-------
Rolf ツ
fonte
11
Isso pode ser 288 se você remover o espaço char[]r=a[0].replace("|", <--here"").toCharArray().
bcsb1001
11
Não localizou esse, obrigado! Bem, isso faz 298
Rolf #:
2

APL, 92

('.-',⎕D)[1+(M≠0)+M{(1+⍺)×0≠⍺∧M[J/⍨Z∊⍨J←⍵∘+¨(⌽¨,+)(-,+)⊂0 1]∧.≤⍺}¨Z←⍳⍴M←↑{×⍴⍵:(⊂⍎¨⍵),∇⍞⋄⍬}⍞]

Exemplo:

       ('.-',⎕D)[1+(M≠0)+M{(1+⍺)×0≠⍺∧M[J/⍨Z∊⍨J←⍵∘+¨(⌽¨,+)(-,+)⊂0 1]∧.≤⍺}¨Z←⍳⍴M←↑{×⍴⍵:(⊂⍎¨⍵),∇⍞⋄⍬}⍞]
34565432100100000000
45676543210000000000
56787654321000000110
45676543210000001221
34565432100000012321
23454321000000123432
12343210000001234543
01232100000012345654
00121000000011234543
00010000000121123432

---------..1........
----------..........
---8-------......--.
----------......--2-
---------......-----
--------......------
-------......-------
.-----......-----6--
..---.......--------
...-.......-2-------
marinus
fonte
O programa de APL mais longo que eu já vi. Você pode observar que esse não é um APL padrão, pois usa dfns.
FUZxxl
2

Ruby 140

f=->s{
r=s.dup
l=s.index(?\n)+1
(0...s.size).map{|i|
s[i]<?0||r[i]=r[i]<?1??.:[i-1,i+1,i-l,i+l].map{|n|n<0??0:s[n]||?0}.max>r[i]??-:s[i]}
r}

Nada especial; basta percorrer o mapa e comparar o valor atual com o valor dos quatro vizinhos.

Execute-o online com testes: http://ideone.com/AQkOSY

Cristian Lupascu
fonte
1

R, 223

Sobre o melhor que posso encontrar no momento. Lidar com a corda é bastante caro. Eu acho que há espaço para melhorias, mas não posso vê-lo no momento

s=strsplit;a=c(m<-do.call(rbind,s(s(scan(w="c"),'|',T)[[1]],'')));w=(d<-dim(m))[1];n=c(-1,1,-w,w);cat(t(array(sapply(seq(a),function(x)if(a[x]>0)if(any(a[(n+x)[which(n+x>0)]]>a[x]))'-'else a[x]else'.'),d)),fill=d[2],sep='')

Resultado do teste

> s=strsplit;a=c(m<-do.call(rbind,s(s(scan(w="c"),'|',T)[[1]],'')));w=(d<-dim(m))[1];n=c(-1,1,-w,w);cat(t(array(sapply(seq(a),function(x)if(a[x]>0)if(any(a[(n+x)[which(n+x>0)]]>a[x]))'-'else a[x]else'.'),d)),fill=d[2],sep='')
1: 898778765432100|787667654321100|677656543211210|678765432112321|567654321123210
2: 
Read 1 item
-9---8-------..
-------------..
--------------.
--8---------3--
-----------3--.
> s=strsplit;a=c(m<-do.call(rbind,s(s(scan(w="c"),'|',T)[[1]],'')));w=(d<-dim(m))[1];n=c(-1,1,-w,w);cat(t(array(sapply(seq(a),function(x)if(a[x]>0)if(any(a[(n+x)[which(n+x>0)]]>a[x]))'-'else a[x]else'.'),d)),fill=d[2],sep='')
1: 34565432100100000000|45676543210000000000|56787654321000000110|45676543210000001221|34565432100000012321|23454321000000123432|12343210000001234543|01232100000012345654|00121000000011234543|00010000000121123432
2: 
Read 1 item
---------..1........
----------..........
---8-------......--.
----------......--2-
---------......-----
--------......------
-------......-------
.-----......-----6--
..---.......--------
...-.......-2-------
> 
MickyT
fonte
1

J - 69 bytes

[:u:45+[:(+2 0 3{~"#1+*)@((]*]=(0,(,-)1 0,:0 1)>./@:|.])-0=])"."0;._2

Exemplos:

   ([:u:45+[:(+2 0 3{~"#1+*)@((]*]=(0,(,-)1 0,:0 1)>./@:|.])-0=])"."0;._2) (0 : 0)
34565432100100000000
45676543210000000000
56787654321000000110
45676543210000001221
34565432100000012321
23454321000000123432
12343210000001234543
01232100000012345654
00121000000011234543
00010000000121123432
)
---------..1........
----------..........
---8-------......--.
----------......--2-
---------......-----
--------......------
-------......-------
.-----......-----6--
..---.......--------
...-.......-2-------
   ([:u:45+[:(+2 0 3{~"#1+*)@((]*]=(0,(,-)1 0,:0 1)>./@:|.])-0=])"."0;._2) (0 : 0)
898778765432100
787667654321100
677656543211210
678765432112321
567654321123210
)
-9---8-------..
-------------..
--------------.
--8---------3--
-----------3--.

PS: (0 : 0)é a maneira J padrão de especificar strings. Você também pode usar |cadeias delimitadas (com um final |).

jpjacobs
fonte
1

Excel VBA - 426

Será uma ocasião rara em que o VBA vença qualquer jogo de código de golfe, mas como é o que eu mais uso, é divertido brincar com ele. A primeira linha é um caso de borda que fez isso mais longo do que parece que deveria ser.

Sub m(a)
    b = InStr(a, "|")
    For i = 1 To Len(a)
        t = Mid(a, i, 1)
        Select Case t
            Case "|"
                r = r & "|"
            Case 0
                r = r & "."
            Case Else
                On Error Resume Next
                x = Mid(a, i - 1, 1)
                y = Mid(a, i + 1, 1)
                Z = Mid(a, i + b, 1)
                If i < b Then
                    If t < x Or t < y Or t < Z Then
                        r = r & "-"
                    Else
                        r = r & t
                    End If
                Else
                    If t < x Or t < y Or t < Z Or t < Mid(a, i - b, 1) Then
                        r = r & "-"
                    Else
                        r = r & t
                    End If
                End If
        End Select
    Next
    MsgBox r
End Sub

A contagem não inclui o espaço em branco da linha inicial.

Eu brinquei com a idéia de enviar a entrada para uma planilha e trabalhar a partir daí, mas acho que o loop da string passada, caractere por caractere, salva o código.

Chamada a partir da janela imediata:

m "34565432100100000000|45676543210000000000|56787654321000000110|45676543210000001221|34565432100000012321|23454321000000123432|12343210000001234543|01232100000012345654|00121000000011234543|00010000000121123432"

Saída (em uma janela):

---------..1........|----------..........|---8-------......--.|----------......--2-|---------......-----|--------......------|-------......-------|.-----......-----6--|..---.......--------|...-.......-2-------
phrebh
fonte
1

Perl - 226

sub f{for(split'
',$_[0]){chomp;push@r,r($_);}for(t(@r)){push@y,r($_)=~s/0/./gr}$,=$/;say t(@y);}sub r{$_[0]=~s/(?<=(.))?(.)(?=(.))?/$1<=$2&&$3<=$2?$2:$2eq'0'?0:"-"/ger;}sub t{@q=();for(@_){for(split//){$q[$i++].=$_;}$i=0;}@q}

Você pode experimentá-lo em ideone . Se alguém estiver interessado em uma explicação, me avise.

hmatt1
fonte
Acho que você tem 226 caracteres, não 227.
Cristian Lupaşcu
@ w0lf Você está certo, a nova linha foi contada por 2 desde que eu estou no Windows.
hmatt1
1

Haskell - 193

z='0'
r=repeat z
g s=zipWith3(\u t d->zip3(zip(z:t)u)t$zip(tail t++[z])d)(r:s)s$tail s++[r]
f=unlines.map(map(\((l,u),t,(r,d))->case()of _|t==z->'.'|maximum[u,l,t,r,d]==t->t|0<1->'-')).g.lines

f é uma função que pega uma string no formulário 0001\n0000\n0000\n1000 e retorna a string necessária.

g é uma função que pega uma lista de listas de caracteres e retorna uma lista de listas de ((esquerda, cima), isto, (direita, baixo)).

Jmac
fonte