2010-07-26 7 views
0

Quel est le moyen le plus rapide de lire à partir de STDIN un nombre de 1000000 caractères (entiers), et le diviser en un tableau d'entiers d'un caractère (pas de chaînes)?ruby ​​lecture rapide de std

123456 > [1,2,3,4,5,6] 

Répondre

2

Cela devrait être assez rapide:

a = [] 
STDIN.each_char do |c| 
    a << c.to_i 
end 

bien que certains étalonnage bruts montre cette version hackish est beaucoup plus rapide:

a = STDIN.bytes.map { |c| c-48 } 
+0

de Nice, beaucoup plus rapide que la méthode obtient, grâce – astropanic

+0

bien aussi, mais comment avez-vous le suppriment -38 à la fin? – astropanic

+0

Vous pouvez exécuter '[0 ...- 1]' sur le tableau résultant, mais généralement, sauf si vous en avez absolument besoin pour être aussi rapide que possible, je vous conseille d'utiliser une validation, en utilisant 'select' ou' rejeter », par exemple. Si la vitesse est critique, vous ne devriez pas utiliser Ruby pour commencer. –

0
scan(/\d/).map(&:to_i) 

Cela diviser une chaîne en un tableau des entiers, en ignorant tous les caractères non numériques. Si vous voulez saisir l'entrée utilisateur de STDIN ajouter obtient:

gets.scan(/\d/).map(&:to_i) 
5

La méthode la plus rapide que je l'ai trouvé à ce jour est la suivante: -

gets.unpack("c*").map { |c| c-48} 

Voici quelques résultats de l'analyse comparative la plupart des solutions proposées . Ces tests ont été exécutés avec un fichier de 100 000 chiffres mais avec 10 répétitions pour chaque test.

 

            user  system  total  real 
each_char_full_array:   1.780000 0.010000 1.790000 ( 1.788893) 
each_char_empty_array:  1.560000 0.010000 1.570000 ( 1.572162) 
map_byte:      0.760000 0.010000 0.770000 ( 0.773848) 
gets_scan      2.220000 0.030000 2.250000 ( 2.250076) 
unpack:      0.510000 0.020000 0.530000 ( 0.529376) 

Et voici le code qui les produit

#!/usr/bin/env ruby 

require "benchmark" 

MAX_ITERATIONS = 100000 
FILE_NAME = "1_million_digits" 

def build_test_file 
    File.open(FILE_NAME, "w") do |f| 
    MAX_ITERATIONS.times {|x| f.syswrite rand(10)} 
    end 
end 

def each_char_empty_array 
    STDIN.reopen(FILE_NAME) 
    a = [] 
    STDIN.each_char do |c| 
    a << c.to_i 
    end 
    a 
end 

def each_char_full_array 
    STDIN.reopen(FILE_NAME) 
    a = Array.new(MAX_ITERATIONS) 
    idx = 0 
    STDIN.each_char do |c| 
    a[idx] = c.to_i 
    idx += 1 
    end 
    a 
end 

def map_byte() 
    STDIN.reopen(FILE_NAME) 
    a = STDIN.bytes.map { |c| c-48 } 
    a[-1] == -38 && a.pop 
    a 
end 

def gets_scan 
    STDIN.reopen(FILE_NAME) 
    gets.scan(/\d/).map(&:to_i) 
end 


def unpack 
    STDIN.reopen(FILE_NAME) 
    gets.unpack("c*").map { |c| c-48} 
end 

reps = 10 
build_test_file 
Benchmark.bm(10) do |x| 
    x.report("each_char_full_array: ") { reps.times {|y| each_char_full_array}} 
    x.report("each_char_empty_array:") { reps.times {|y| each_char_empty_array}} 
    x.report("map_byte:    ") { reps.times {|y| map_byte}} 
    x.report("gets_scan    ") { reps.times {|y| gets_scan}} 
    x.report("unpack:    ") { reps.times {|y| unpack}} 
end 
+0

quel est le moyen le plus rapide de prendre stdin de lignes de coordonnées comme 0 10 \ n 89 23 \ n 45 32 \ n 3 54 etc et de créer un objet ponctuel de chacun ainsi Point.new (firstnum, secondnum) – Evan

Questions connexes