йНМЯОЕЙР ОН МЮВЕПРЮРЕКЭМНИ ЦЕНЛЕРПХХ хМФЕМЕПМЮЪ ЦПЮТХЙЮ юПУХРЕЙРСПЮ щбл хМТНПЛЮРХЙЮ Х ХМТНПЛЮЖХНММШЕ РЕУМНКНЦХХ

Предыдущий разделУровень вышеСледующий раздел

Примеры программ

НОД и алгоритм Евклида

При рассмотрении задач, связанных с обработкой целых чисел, часто приходится сталкиваться с понятием НОД - наибольшего общего делителя чисел. Известно много алгоритмов вычисления НОД, мы рассмотрим лишь два из них.


Задача
Напишите программу, вычисляющую НОД(a,b) - наибольший общий делитель двух введенных с клавиатуры неотрицательных целых чисел a и b, не равных нулю одновременно.


Вариант 1
Определим максимум из этих чисел и, последовательно уменьшая его, будем искать число, на которое делятся и a и b. Программа обязательно завершит свою работу, так как в самом неудачном случае, когда эти числа взаимно просты, k станет равно 1, а на 1 делятся все числа, следовательно выполнение цикла прекратится.

 print "Введите первое 
число: "; a = gets.to_i print "Введите второе число: "; b = gets.to_i k = a >= 
b ? a : b # теперь k - максимум until (a % k == 0) and (b % k == 0) k -= 1 end 
print "НОД(#{a},#{b}) = #{k}\n" 


Вариант 2 - алгоритм Евклида
Алгоритм Евклида основан на следующих свойствах НОД: для всех a и b, больших или равных 0, выполнены равенства

 НОД(a, b) = НОД(a - b, b)= НОД(a, 
b - a); НОД(a, 0) = НОД(0, a) = a 
Ниже приведен текст программы, реализующей алгоритм Евклида.
 print "Введите первое число: "; a = gets.to_i print "Введите 
второе число: "; b = gets.to_i m, n = a, b while !((m == 0) || (n == 0)) if m 
>= n m = m - n else n = n - m end end k = m == 0 ? n : m print "НОД(#{a},#{b}) 
= #{k}\n" 

(Загрузить файл с примером)

Аргументы командной строки

Как и другие языки программирования, Ruby позволяет передавать данные в программу, использую аргументы командной строки. Все, что при старте программы указывается после ее имени, интерпретатор помещает в специальный массив ARGV, работа с элементами которого нечем не отличается от работы с другими массивами. Можно, например, проверить, с правильным ли количеством аргументов командной строки была вызвана программа. Далее приводится программа, прерывающий свое выполнение, если число аргументов меньше 2:
 
=begin Напишите программу подсчета суммы всех нечетных чисел, заключенных в интервале 
от K до L, где K и L - аргументы командной строки =end if ARGV.length < 2 puts 
"Слишком мало входных данных" exit(1) end s = 0 k = ARGV[0].to_i l = ARGV[1].to_i 
for i in k .. l s += i if i%2 != 0 end puts "Сумма нечетных чисел в интервале 
" + "от #{k} до #{l} равна #{s}" 
Для экстренного прерывания процесса выполнения программы здесь использован встроенный метод exit.

(Загрузить файл с примером)


Задача
Числами Фибоначчи называется последовательность целых чисел, задаваемая соотношениями: а0 = 0, a1 = 1, an = an-1+an-2. Напишите программу, получающую с качестве аргумента командной строки целое число n, и печатающую n-е число Фибоначчи.


Вариант 1 - рекурсивная функция
В следующей программе первый элемент массива ARGV преобразовывается в целое число и передается в качестве аргумента рекурсивной функции fib.

 def fib(n) if n<2 n else fib(n-2)+fib(n-1) end end n = ARGV[0].to_i puts 
"#{n}-е число Фибоначчи равно #{fib(n)}" 
Для вычисления с помощью этой программы, размещенной в файлe fib.rb, например, 30-го числа Фибоначчи достаточно выполнить команду
 ruby fib.rb 30 
Данная программа использует двойную рекурсию, поэтому процесс вычисления займет достаточно заметное время.

(Загрузить файл с программой)


Вариант 2 - использование массива
В следующем варианте решения этой задачи каждое число Фибоначчи вычисляется лишь один раз и сразу заносится в массив. Для определения очередного элемента массива достаточно сложить два предыдущих.

 n =ARGV[0].to_i f= [0, 
1] fib = case n when 0 f[0] when 1 f[1] else for i in 2 .. n f[i] = f[i-1] + f[i-2] 
end f[n] end puts "#{n}-е число Фибоначчи равно #{fib}" 

(Загрузить файл с программой)

Однако и у этой программы есть существенный недостаток - она хранит все промежуточные результаты, в то время как каждое предыдущее значение используется лишь дважды. Изменим программу так, чтобы она использовала только три переменных: вычисляемое значение и два предшествующих ему:

 n =ARGV[0].to_i if n > 1 prev, beforePrev = 1, 0 for i in 2 .. n fib 
= prev + beforePrev prev, beforePrev = fib, prev end else fib = n end puts "#{n}-е 
число Фибоначчи равно #{fib}" 

(Загрузить файл с программой)

Включите в рассмотренные программы операторы, информирующие о продолжительности вычислений, и сравните их эффективность. Для этого найдите с помощью первой из рассмотренных программ 30, 31 и 32 числа Фибоначчи. Пользуясь второй и, тем более, третьей вы сможете вычислить достаточно большое число Фибоначчи, например, для вычисления 60000-го числа Фибоначчи, содержащего 12539 цифр на компьютере с процессором Celeron-500, программе, размещающей промежуточные результаты в массиве, потребовалось около 19 секунд, в то время как последняя программа вычислила его за 2 секунды.

Работа с файлами

Ввод данных с клавиатуры в процессе выполнения программы удобен только в случае небольших объемов. В других ситуациях необходимо чтение информации из заранее подготовленного файла.

Для работы с файлом при помощи метода new создается экземпляр класса File. Обязательным аргументом этого метода является строка, содержащая имя файла, например,

 f = File.new("myfile.txt") 
Другим (необязательным) аргументом является задание режима работы с файлом. По умолчанию этот параметр имеет значение "r", что соответствует режиму "только чтение". Если требуется открыть файл с возможностью записи в него, то следует указать параметр "w", например,
 f1 = File.new("newfile.txt", "w") 

Метод readlines считывает весь файл, создает массив и размещает каждую прочитанную строку в отдельном элементе массива При дальнейшей обработке полученного массива удобно использовать метод each.

Для вывода строки в выходной поток (в файл или на экран монитора) используется метод write, например,

 a = "Hello, world!" 
f1.write(a) 


Задача
Имеется текстовый файл fio.txt, содержащий список фамилий, имен и отчеств учащихся. При этом каждая строка файла содержит данные только об одном человеке, например,

 Петров Сергей Васильевич 
Сидорова Ольга Петровна Иванова Марья Даниловна 
Напишите программу, которая читает информацию из файла и
1) печатает пронумерованный список учеников;
2) печатает пронумерованный список фамилий и инициалов.


Решение 1

 f = File.new("fio.txt") n = 1 student = f.readlines student.each{ 
|i| print n, ". ", i n += 1 } 
Отметим, что программа должна располагаться в той же директории, что и файл с данными. В противном случае необходимо указывать полный путь к этому файлу.

Для получения инициалов каждую строку полученного массива преобразуем в массив, элементами которого будут фамилия, имя и отчество. Затем фамилию печатаем полностью, а вместо следующих элементов массива - только их первый символ. Для преобразования строки в массив используется ранее рассмотренная функция split. Напомним, что параметром по умолчанию этой функции является пробел.


Решение 2

 f = File.new("fio.txt") n = 1 student = 
f.readlines student.each{ |i| i.chop! fio = i.split print n, ". ", fio[0], " ", 
fio[0][0].chr, ". ", fio[1][0].chr, ".\n" n += 1 } 

(Загрузить файл с примером)

Случайные числа

Моделирование случайных процессов на компьютере - достаточно распространенное явление. Как и в большинстве языков программирования в Ruby есть методы, генерирующие, так называемые, "псевдослучайные" числа (их нельзя считать чисто случайными, ведь создаются они при помощи некоторого алгоритма).

Для получения случайного целого числа в диапазоне от 0 до n используется метод rand(n). Случайное число в диапазоне от 0 до 1 получается с помощью этого же метода без указания параметра.

 a1 = 
(1..10).collect { |j| rand(100)} a2 = (1..4).collect { |j| rand()} p a1, a2 
Ниже представлен возможный вывод:
 [64, 17, 21, 95, 58, 24, 29, 60, 47, 63] [0.2009671596, 
0.8890923676, 0.3349569312, 0.8719313448] 

Одним из применений случайных чисел является компьютерное моделирование метода Монте-Карло для определения площади некоторой фигуры, рассмотренный нами ранее. Напомним, что для применения этого метода фигуру вписывают в другую, известной площади, и случайным образом "бросают" точки, подсчитывая число попаданий в фигуру. При достаточно большом числе испытаний отношение числа точек, попавших внутрь фигуры, к общему числу точек, стремится к отношению их площадей.


Задача
Напишите программу, вычисляющее приближенное значение числа PI при помощи метода Монте-Карло.


Решение
Число PI равно отношению площади круга к квадрату его радиуса. Впишем круг единичного радиуса с центром в начале координат в квадрат и методом Монте-Карло найдем его площадь. Координаты случайных точек, бросаемых в круг, принадлежат интервалу (-1; 1). Величина, случайно распределенная в этом интервале, задается выражением 2*rand() - 1.

Для контроля за временем добавим два экземпляра класса Time: время начала и окончания расчета. Программа, реализующая этот алгоритм, представлена ниже. В ней используется оператор eval, чтобы количество точек можно было задавать в виде арифметического выражения.

 puts "Введите количество 
точек:" n1, n, t1 = 0, eval(gets.chop), Time.now for i in 1 .. n x = 2*rand() 
- 1 y = 2*rand() - 1 # проверяем попадание внутрь круга n1 += 1 if (x**2 + y**2) 
< 1 end puts "PI=#{4.0*n1/n}" t2 = Time.now puts "Число точек #{n}, время расчета 
" + "около #{(t2 - t1).round} сек." 
Возможные результаты работы программы будут похожи (случайность!) на следующие:
 Введите количество точек: 
10**5 PI=3.14028 Число точек 100000, время расчета около 2 сек. Введите количество 
точек: 10**6 PI=3.142204 Число точек 1000000, время расчета около 20 сек. 

(Загрузить файл с примером)

Разработка пользовательских классов

Пример программы на языке Ruby, демонстрирующий работу с классами, уже приводился в главе 9. Следующий пример демонстрирует использование так называемой переменной класса, которая не принадлежит ни одному из экземпляров данного класса, а используется для хранения информации, относящейся ко всему классу в целом.


Задача
Напишите программу, описывающую класс автомобиль и два его дочерних класса: спортивные машины и машины для семейного использования. В программе нужно предусмотреть возможность печати общего числа созданных автомобилей.


Решение


Поместите в файл с именем car.rb следующую программу:

 class Car @@NUM_CARS 
= 0 def initialize @@NUM_CARS = @@NUM_CARS + 1 puts @@NUM_CARS end end class SportsCar 
< Car end class FamilyCar < Car end a = Car.new b = SportsCar.new c = FamilyCar.new 
Для запуска программы выполните команду ruby car.rb. При создании каждого автомобиля, независимо от того, к какому (родительскому или дочернему) классу он принадлежит, значение переменной класса @@NUM_CARS увеличивается на единицу.

(Загрузить файл с примером)


Задания

  1. Напишите программу, запрашивающую с клавиатуры натуральное число, большее 1, и печатающую список всех простых несократимых дробей, заключенных в интервале между 0 и 1, знаменатели которых не превышают введенное число. Например, если ввести число 4, то должна быть напечатана последовательность 1/2, 1/3, 2/3, 1/4, 3/4. Подсказка: используйте функцию нахождения НОД.
  2. Создайте текстовый файл, в котором разместите фамилии учащихся и их рост в сантиметрах. Напишите программу
    а) печатающую фамилию и рост самых высоких учеников;
    б) по введенному числу - росту с сантиметрах - печатающую список всех учащихся, чей рост не превышает введенного числа.
  3. Напишите программу, вычисляющую методом Монте-Карло площадь криволинейной трапеции, ограниченной графиками функций y=sin(x) и y=0 (x изменяется в интервале от 0 до PI).

Предыдущий разделУровень вышеСледующий раздел

мЮВЕПРЮРЕКЭМЮЪ ЦЕНЛЕРПХЪ Х ХМФЕМЕПМЮЪ ЦПЮТХЙЮ, ОЕПЯОЕЙРХБЮ