1
+ // Adriano Roberto de Lima
2
+ // Solução do desafio 11 - osprogramadores.com
3
+ // Otimizada para rodar o mais rapido possível
4
+ // Processa as sequências sem espaços entre os primos encontrados
5
+
6
+ package main
7
+
8
+ import (
9
+ "flag"
10
+ "fmt"
11
+ "io/ioutil"
12
+ "log"
13
+ "math"
14
+ "os"
15
+ "runtime"
16
+ "runtime/pprof"
17
+ )
18
+
19
+ // Estrutura para ser enviada no canal de requests da função startWorkers.
20
+ type (
21
+ sRequest struct {
22
+ posInicial int
23
+ palavra []byte
24
+ posicao int
25
+ sequencia string
26
+ dados []byte
27
+ lenDados int
28
+ testaPrimo []bool
29
+ lenMaiorSeq int
30
+ }
31
+
32
+ // Estrutura que vamos usar para retornar as sequencias e a posição onde foram
33
+ // encontradas. Precisamos registrar as posições pois não sabemos a ordem
34
+ // de finalização dos requests que serão processados pelos workers (goroutines)
35
+ sSequencia struct {
36
+ sequencia string
37
+ posInicial int
38
+ }
39
+ )
40
+
41
+ // geraprimos é uma função para geração de um array que identifica se um
42
+ // número é primo ou não. Vamos usar esse array para acelerar a identificação
43
+ // de números primos na busca das soluções.
44
+ // Referência: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
45
+ // Só vamos rodar uma vez.
46
+ func geraPrimos (n int64 ) []bool {
47
+ var (
48
+ i , j int64
49
+ )
50
+
51
+ flag1 := make ([]bool , n )
52
+ for i = 0 ; i < n ; i ++ {
53
+ flag1 [i ] = true
54
+ }
55
+
56
+ flag1 [0 ] = false
57
+ flag1 [1 ] = false
58
+
59
+ for i = 2 ; i < int64 (math .Sqrt (float64 (n )))+ 1 ; i ++ {
60
+ if flag1 [i ] {
61
+ for j = i * i ; j < n ; j += i {
62
+ flag1 [j ] = false
63
+ }
64
+ }
65
+ }
66
+ return flag1
67
+ }
68
+
69
+ // palavraToInt converte um array de bytes para um inteiro. Não faz nenhum tipo
70
+ // de verificação. Serve para converter a palavra que está na função "geraSequencias".
71
+ // É bem mais rápido do que fazer um strconv.Atoi(string(b))
72
+ func palavraToInt (b []byte ) int {
73
+ n := 0
74
+ for _ , ch := range b {
75
+ ch -= '0'
76
+ n = n * 10 + int (ch )
77
+ }
78
+ return n
79
+ }
80
+
81
+ // geraSequencias é a função utilizada para encontrar sequencias de numeros primos,
82
+ // de no máximo 4 digitos, maiores que lenMaiorSeq, partindo de uma posição p
83
+ // de uma sequência de digidos "dados".
84
+ func geraSequencias (posInicial int , palavra []byte , posicao int , sequencia string , dados []byte , lenDados int , testaPrimo []bool , lenMaiorSeq int ) []sSequencia {
85
+ ret := []sSequencia {}
86
+ novapalavra := append (palavra , dados [posicao ])
87
+
88
+ // Se o tamanho da palavra é maior que quatro bytes então já ultrapassamos o limite
89
+ // para encontrar um numero primo. Por isso
90
+ // retornamos a sequência que encontramos se ela for maior ou igual a lenMaiorSeq.
91
+ if posicao >= lenDados || len (novapalavra ) > 4 {
92
+ if len (sequencia ) >= lenMaiorSeq {
93
+ ret = append (ret , sSequencia {sequencia , posInicial })
94
+ }
95
+ return ret
96
+ }
97
+
98
+ // Aqui disparamos a busca por novas soluções considerando a novapalavra, partindo
99
+ // da nova posição e a sequencia que já temos.
100
+ r := geraSequencias (posInicial , novapalavra , posicao + 1 , sequencia , dados , lenDados , testaPrimo , lenMaiorSeq )
101
+ ret = append (ret , r ... )
102
+
103
+ // Se a nova palavra for um primo, podemos colocá-la na sequencia e continuar
104
+ // a busca por novas palavras válidas.
105
+ if testaPrimo [palavraToInt (novapalavra )] {
106
+ r = geraSequencias (posInicial , []byte {}, posicao + 1 , sequencia + string (novapalavra ), dados , lenDados , testaPrimo , lenMaiorSeq )
107
+ ret = append (ret , r ... )
108
+ }
109
+
110
+ return ret
111
+ }
112
+
113
+ // worker é a função que faz as chamadas da nossa função geraSequencias usando
114
+ // os canais de entrada e retornando as respostas nos canais de saída.
115
+ // Eles ficam sempre ativos esperando jobs (ou requests) chegarem para serem
116
+ // processados.
117
+ func worker (id int , req <- chan sRequest , res chan <- []sSequencia ) {
118
+ for c := range req {
119
+ r := geraSequencias (c .posInicial , c .palavra , c .posicao , c .sequencia , c .dados , c .lenDados , c .testaPrimo , c .lenMaiorSeq )
120
+ res <- r
121
+ }
122
+ }
123
+
124
+ // startWorkers é a nossa função que dispara os workers, dispara os requests para
125
+ // os workers e consolida as respostas que vem pelos canais de saída. Retorna todas
126
+ // as sequencias encontradas.
127
+ func startWorkers (workers int , batchSize int , dados []byte , lenDados int ) []sSequencia {
128
+ var (
129
+ sequencias []sSequencia
130
+ batchResults []sSequencia
131
+ )
132
+
133
+ lenMaiorSeq := 1
134
+ testaPrimo := geraPrimos (10000 )
135
+ jobs := make (chan sRequest , batchSize ) // Bufferizado no tamanho do batch.
136
+ results := make (chan []sSequencia , batchSize ) // Bufferizado no tamanho do batch.
137
+
138
+ // Colocando no ar todos os workers.
139
+ for w := 0 ; w < workers ; w ++ {
140
+ go worker (w , jobs , results )
141
+ }
142
+
143
+ // Disparando os jobs para serem processados pelos workers. Vamos disparar
144
+ // em lotes de tamanho batchSize para podermos pegar resultados intermediarios.
145
+ // Fazemos isso para encontrar o tamanho da maior sequencia e minimizar a
146
+ // quantidade de sequencias obtidas na funçao recursiva. Nos interessam somente
147
+ // as maiores.
148
+
149
+ a := 2 //Todo arquivo começa com "3." . Precisamos desconsiderar.
150
+ for a < lenDados {
151
+ if a + batchSize >= lenDados {
152
+ batchSize = lenDados - a
153
+ }
154
+ for b := 0 ; b < batchSize ; b ++ {
155
+ req := sRequest {
156
+ posInicial : a + b ,
157
+ palavra : []byte {},
158
+ posicao : a + b ,
159
+ sequencia : "" ,
160
+ dados : dados ,
161
+ lenDados : lenDados ,
162
+ testaPrimo : testaPrimo ,
163
+ lenMaiorSeq : lenMaiorSeq ,
164
+ }
165
+ jobs <- req
166
+ }
167
+
168
+ // Coletando os resultados dos canais de saída.
169
+ batchResults = []sSequencia {}
170
+ for b := 0 ; b < batchSize ; b ++ {
171
+ r := <- results
172
+ batchResults = append (batchResults , r ... )
173
+ }
174
+
175
+ // Coletando apenas as maiores sequencias e o valor de lenMaiorSeq para usar.
176
+ // nos proximos batches
177
+ for _ , v := range batchResults {
178
+ l := len (v .sequencia )
179
+ if l > lenMaiorSeq {
180
+ lenMaiorSeq = l
181
+ sequencias = []sSequencia {v }
182
+ } else {
183
+ if l == lenMaiorSeq {
184
+ sequencias = append (sequencias , v )
185
+ }
186
+ }
187
+ }
188
+ a += batchSize
189
+ }
190
+ return sequencias
191
+ }
192
+
193
+ func main () {
194
+ var (
195
+ optCPUProfile string
196
+ optBatchSize int
197
+ )
198
+
199
+ runtime .GOMAXPROCS (runtime .NumCPU ())
200
+ log .SetFlags (0 )
201
+
202
+ flag .StringVar (& optCPUProfile , "cpuprofile" , "" , "escreve a profile de cpu em arquivo" )
203
+ flag .IntVar (& optBatchSize , "batchsize" , 0 , "tamanho do batch de processamento paralelo" )
204
+ flag .Parse ()
205
+
206
+ if len (flag .Args ()) != 1 {
207
+ log .Fatalln ("Use: desafio 11 <arquivo>" )
208
+ }
209
+
210
+ if optCPUProfile != "" {
211
+ f , err := os .Create (optCPUProfile )
212
+ if err != nil {
213
+ log .Fatal (err )
214
+ }
215
+ pprof .StartCPUProfile (f )
216
+ defer pprof .StopCPUProfile ()
217
+ }
218
+
219
+ batchSize := 1000
220
+ if optBatchSize != 0 {
221
+ batchSize = optBatchSize
222
+ }
223
+
224
+ dados , err := ioutil .ReadFile (flag .Args ()[0 ])
225
+ if err != nil {
226
+ fmt .Println ("ERRO! Não consegui ler o arquivo !" )
227
+ log .Fatal (err )
228
+ }
229
+
230
+ lenDados := len (dados )
231
+ // Adicionando alguns bytes ao final dos dados para gaarantir que toda
232
+ // a sequência será tratada.
233
+ extras := []byte {0 , 0 , 0 , 0 }
234
+ dados = append (dados , extras ... )
235
+
236
+ // Começando o trabalho de busca das soluções.
237
+ sequencias := startWorkers (runtime .NumCPU (), batchSize , dados , lenDados )
238
+
239
+ // Procurando a maior sequencia na menorPosicao.
240
+ menorPosicao := lenDados
241
+ sequenciaMenorPosicao := ""
242
+ for _ , v := range sequencias {
243
+ if v .posInicial < menorPosicao {
244
+ menorPosicao = v .posInicial
245
+ sequenciaMenorPosicao = v .sequencia
246
+ }
247
+ }
248
+
249
+ fmt .Println (sequenciaMenorPosicao )
250
+
251
+ fmt .Print ("\n \n " )
252
+ }
0 commit comments