3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ruby推し】島根出身者がPaizaのC問題で初心者向けにプログラミング言語を比較してみた

Last updated at Posted at 2025-02-09

この記事の目的と対象読者

「プログラミングを始めたいけど、どの言語を選べばいいか迷いますよね?」「Paizaには様々な問題がありますが、どの言語で解くのが簡単なのでしょうか?」

この記事では、やや複雑なC問題「自然の残る公園」の解説を5つのプログラミング言語で行い、初学者の方がどのプログラミング言語を選択するかの手助けとなるような記事を目指しています。

わし、島根のもんだけん、記事の内容がちぃとRuby寄りになっちょるかもしれんけん、気にせんでごしないよ。
(私は島根出身です(本当)。記事の内容がRubyに偏っているかもしれませんが、ご容赦ください。)

言語の選定

次の記事を参考にして、この記事でとりあげる言語を選定しました。

【プログラミング言語調査】平均年収が高い言語・企業ニーズが高い言語ランキング【2023年版】

この中の上位4つに特別枠のRubyを加えた5つをこの記事で取り上げます。

各言語の説明

  • Java (定番の言語、Paizaスキルチェックにはやや不向きか)
  • Ruby (特別枠で参戦、電脳少女でもなぜか島根弁が登場)
  • Python3 (言わずもがな、今大変人気の言語)
  • JavaScript (Web関係を勉強するなら必須。やっておいて損はない)
  • C# (ゲーム開発といえばこれ)
もう少し詳しい言語比較(Google Geminiにより生成)
言語 特徴 メリット デメリット 初心者向け度 使用場面 実際に使用しているアプリケーション/サービス
Java オブジェクト指向、汎用性が高い、大規模開発向き 安定性、信頼性が高い、大規模システム開発でよく使われる、学習リソースが豊富 学習コストが高い、環境構築がやや複雑、記述が冗長になりがち ★★☆☆☆ エンタープライズシステム、Androidアプリ、Webアプリ 大規模基幹システム、Android OS、Minecraft、Netflix (一部)
Ruby スクリプト言語、記述がシンプルで読みやすい、Web開発(Ruby on Rails) 記述が簡潔、開発効率が高い、Ruby on RailsでWebアプリケーションを高速開発できる 実行速度が遅い、情報量がやや少ない、Web開発以外の用途には不向き ★★★☆☆ Webアプリケーション Cookpad、GitHub (一部)、Airbnb (過去)
Python3 スクリプト言語、汎用性が高い、科学技術計算、データ分析 文法がシンプルで読みやすい、豊富なライブラリ(NumPy, Pandasなど)、データ分析でよく使われる 実行速度が遅い ★★★★☆ データ分析、機械学習、Webアプリ、自動化 Google (検索エンジン、YouTube)、Instagram、Dropbox、Netflix (一部)
JavaScript スクリプト言語、Webブラウザ上で動作、Webページの動的な表現 Webブラウザ上で動作、WebページのUI/UXを向上できる、Node.jsでサーバーサイドも開発可能 ブラウザ間の互換性に注意が必要、セキュリティに注意が必要 ★★★★☆ WebページのUI/UX、Webアプリ、サーバーサイド (Node.js) Google (Gmail, Google Maps)、Facebook、YouTube、Twitter
C# オブジェクト指向、Microsoftが開発、Windowsアプリ、ゲーム開発(Unity) Windowsアプリ開発が容易、Unityでゲーム開発ができる、Visual Studioで開発しやすい Windows環境に依存する、学習コストが高い ★★☆☆☆ Windowsアプリ、ゲーム開発 (Unity)、Webアプリ Microsoft製品 (Office, Visual Studio)、多くのゲーム (例: Pokemon Go, Final Fantasy XV)

解説

各言語を比較するために、まずは問題と解答の方針について整理しておきます。

問題とその概要

自分の現在地点(a,b)と、そのほかにN個の地点の座標が与えられる。一番近くはどこか?

距離の計算についての補足

問題文の末尾に(ビーコンの x 座標 - 現在地の x 座標 ) ^ 2 + (ビーコンの y 座標 - 現在地の y 座標) ^ 2 という記載があります。これは距離の2乗の計算式です。今回は距離の2乗を用いても正しい答えを得ることができますが、各言語の比較をするために、ルートをとった正しい距離の値を使用することにします。

解答の方針

自分と各地点との距離を計算して、その中で最小のものを答えればよい。
プログラムの流れは次のようになる。

  • N+1行の入力を受け取る
    • 標準入力から N, a, b の値を受け取る
    • N個の地点の座標データを格納するための配列(リスト)を用意する
    • N個の地点の座標データを、標準入力から読み込み、配列に格納する
  • 1番目の地点との距離d1を計算
    • 現在地の座標(a, b)と1番目の地点の座標(y1, x1)を取得する
    • 2点間の距離を計算する公式を用いて、距離d1を計算する
    • 計算結果を距離を格納するための配列に保存する
  • 2 ~ N 番目の地点との距離 d2 ~ dn を同様に計算
  • d1 〜 dn のうち、一番小さい地点の番号を解答(一番近い距離ではないので注意)
    • 距離を格納した配列の中から、最小値を探索する
    • 最小値を持つ要素のインデックス(地点の番号)を取得する
    • 取得した地点の番号を答えとして標準出力に出力する

疑似コード

入力:N, a, b, (y1, x1), (y2, x2), ..., (yN, xN)
距離を格納する配列 distances を用意
For i = 1 to N:
    距離 d[i] = (a - yi)^2 + (b - xi)^2  // 2点間の距離の2乗を計算
    distances[i] に d[i] を格納
End For

min_distance = distances[1]  // 最初の距離を最小値として仮定
min_index = 1             // 最小値を持つ地点のインデックス

For i = 2 to N:
    If distances[i] < min_distance:
        min_distance = distances[i]
        min_index = i
    End If
End For

出力:min_index  // 最小距離の地点番号

コードの比較

Paizaスキルチェックでは、言語を選択してコードを提出します。そして、選択したコードに応じた初期コードが自動的に表示されます。まずは、その初期コードを比較してみましょう。(初期入力に含まれるコメントは削除しています)
これらのコードはいずれも、標準入力から1行の値を受け取り、"XXXXXX"という文字列を出力するというものになっています。

入出力の方法も1つの言語でも複数の方法がありますが、今回はこの初期コードに記載されている入出力の方法を使用することにします

各言語のコードが大量に記載されています。適宜読み飛ばすなどしてください。

  • Java
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        System.out.println("XXXXXX");
    }
}

いきなり仰々しいコードが出てきました。でも、最初のimport やpublic class等の記載は「おまじない」と呼ばれるもので、いったん理解することは置いておきましょうというやつですからまぁ気にしないことにしておきましょう。また、文末にセミコロン(;)がついています。

2つ目はこちら。早速の真打登場ですね。

  • Ruby
input_line = gets
puts "XXXXXX"

たった2行!すごい!1行目で入力の受け取り、2行目で出力を行っていることもすぐにわかります。素晴らしいです。

次はPythonです。

  • Python3
input_line = input()
print("XXXXXX")

PythonもRubyと似ていて簡潔です。

次はJavaScirpt.

  • JavaScript
process.stdin.resume();
process.stdin.setEncoding('utf8');
var lines = [];
var reader = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
reader.on('line', (line) => {
  lines.push(line);
});
reader.on('close', () => {
  console.log(lines[0]);
});

これまた長いです。今の所のぱっと見の印象ではJavaScirptが一番複雑そうに見えるかもしれません。こちらもJavaと同様に文末にはセミコロンがあります。

さいごはC#。

  • C#
using System;
class Program
{
    static void Main()
    {
        var line = Console.ReadLine();
        Console.WriteLine("XXXXXX");
    }
}

なんとなくJavaと似ている感じがしますが、Javaよりはすっきりした印象です。こちらにもセミコロンあり。

N+1個の入力の受けとり

では、まずはN+1個の入力を受け取る部分について比較してみましょう。コメントもつけているので参考にしてください。

  • Java
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 標準入力からNの値を受け取る
        int N = scanner.nextInt();
        // 現在地の座標を受け取る
        int a = scanner.nextInt();
        int b = scanner.nextInt();

        // N個の地点の座標データを格納するためのリストを用意する
        List<int[]> beaconCoordinates = new ArrayList<>();

        // N個の地点の座標データを、標準入力から読み込み、リストに格納する
        for (int i = 0; i < N; i++) {
            int y = scanner.nextInt();
            int x = scanner.nextInt();
            beaconCoordinates.add(new int[]{y, x});
        }

        scanner.close(); // Scannerを閉じる (リソースリークを防ぐ)
    }
}

受け取った値を整数として扱うためにnextIntというものを使用しています。(それ以外の点については後述)

  • Ruby
# 標準入力からNの値,現在地の座標を受け取る
N, a, b = gets.chomp.split(" ").map(&:to_i)

# N個の地点の座標データを格納するための配列を用意する
beacon_coordinates = []

# N個の地点の座標データを、標準入力から読み込み、配列に格納する
N.times do
  y, x = gets.chomp.split(" ").map(&:to_i)
  beacon_coordinates << [y, x]
end

Rubyでは、受け取った値を整数に変換するのに、mapというメソッドを使用して、to_i(to integer、つまり整数に変換)を受け取った値すべてに適用しています。(mapは数学の写像と思えばよいでしょう)

  • Python
# 標準入力からNの値,現在地の座標を受け取る
N, a, b = map(int,input().split())

# N個の地点の座標データを格納するためのリストを用意する
beacon_coordinates = []

# N個の地点の座標データを、標準入力から読み込み、リストに格納する
for _ in range(N):
  y, x = map(int, input().split())
  beacon_coordinates.append([y, x])

Pythonもmapを使用しています。mapを使用してintを各入力に適用しているのでRubyと同じような印象です。

  • JavaScript
const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout,
});

let n, a, b;
let beaconCoordinates = [];

readline.question('', input => {
  [n, a, b] = input.split(" ").map(Number);

  let count = 0;
  readline.on('line', line => {
    count++;
    if (count <= n) {
      const [y, x] = line.split(" ").map(Number);
      beaconCoordinates.push([y, x]);
    } else {
      readline.close();
    }
  });
});

JavaScirptもmap.ただし、integerではなくNumberとなっています。N個の入力の受け取りの部分も他と少し違っているように見えます。

  • C#
using System;
using System.Collections.Generic;
using System.Linq;

public class NearestBeacon
{
    public static void Main(string[] args)
    {
        // 標準入力からN, a, bの値を受け取る
        int[] firstLine = Console.ReadLine().Split(" ").Select(int.Parse).ToArray();
        int N = firstLine[0];
        int a = firstLine[1];
        int b = firstLine[2];

        // N個の地点の座標データを格納するためのリストを用意する
        List<int[]> beaconCoordinates = new List<int[]>();

        // N個の地点の座標データを、標準入力から読み込み、リストに格納する
        for (int i = 0; i < N; i++)
        {
            int[] coordinates = Console.ReadLine().Split(" ").Select(int.Parse).ToArray();
            int y = coordinates[0];
            int x = coordinates[1];
            beaconCoordinates.Add(new int[] { y, x });
        }
    }
}

C#では、int.Parseを使用しています。 parseというのは、英語では解析するといった意味ですが、プログラミングでは、ある型の変数を別の型に変換するときにparseすると言ったりします。


整数への変換以外の違い

  • JavaとC#はN, a, b の受け取りが3行に分かれている。(実は1行でも受け取れますが、やや煩雑なので採用しませんでした)
  • Ruby,Python,JavaScriptはsplit()を使用して、1行で入力を受け取っています。(splitは1行の文字列をいくつかに分割(split)する、今の場合は、スペース区切りのN a bをそれぞれに分割する役割)
  • JavaとJavaScriptは close というのが最後に書かれている。(リソースの解放。なくても動作することもありますが、記載推奨)
  • for文の書き方は似ているがJavaScriptだけ別途変数を用意して、それを使用している(let count = 0;)
  • Rubyのみ、forではなくtimesというものを使用している。プログラミングを始めて学習する人にとっては、times のほうが何回も処理を実行するイメージがつきやすいかもしれませんね。

各地点との距離を計算

既存の部分は省略して、各地点との距離を計算する部分のみ載せます。

  • Java
// 距離を格納する配列
double[] distances = new double[n];

// 各ビーコンとの距離を計算
for (int[] coord : beaconCoordinates) {
    int y = coord[0];
    int x = coord[1];
    double distance = Math.sqrt((a - y) * (a - y) + (b - x) * (b - x));
    distances.add(distance);
}
  • Ruby
# 距離を格納する配列
distances = []

# 各ビーコンとの距離を計算
beacon_coordinates.each do |coord|
  y, x = coord[0], coord[1]
  distance = Math.sqrt((a - y)**2 + (b - x)**2)
  distances << distance
end
  • Python
# 距離を格納するリスト
distances = []

# 各ビーコンとの距離を計算
for y, x in beacon_coordinates:
  distance = math.sqrt((a - y)**2 + (b - x)**2)
  distances.append(distance)

Python では、リストというデータ構造を使って、複数のデータをまとめて扱います。これは、他の言語で言うところの配列と似たようなものと思ってください。この記事内では配列とリストを混同しても問題ありません。

  • JavaScript
// 距離を格納する配列
let distances = []; 

  let count = 0;
  readline.on('line', line => {
    count++;
    if (count <= n) {
      const [y, x] = line.split(" ").map(Number);
      beaconCoordinates.push([y, x]);
    } else {
      // 各ビーコンとの距離を計算
      beaconCoordinates.forEach(([y, x]) => {
        const distance = Math.sqrt((a - y) ** 2 + (b - x) ** 2);
        distances.push(distance);
      });

JavaScriptだけ,さきほどの入力の受け取りの部分のelse節で距離の計算を行っています。なぜでしょうか?
Google Geminiに聞いてみました。「JavaScriptではelse節の中で距離の計算を行うのはなぜですか?」

JavaScriptのコードで、else 節の中で距離計算を行う理由は、readline が非同期的に動作するためです。(後略)

ということです。ざっくりと説明すると、else節にしておかないと、入力を受け取っている最中に距離の計算をしてしまって、座標がわからないのに、距離計算してしまう可能性があるから、となります。

  • C#
// 距離を格納する配列
double[] distances = new double[n];

// 各ビーコンとの距離を計算
foreach (int[] coord in beaconCoordinates)
{
    int y = coord[0];
    int x = coord[1];
    double distance = Math.Sqrt((a - y) * (a - y) + (b - x) * (b - x));
    distances.Add(distance);
}   

距離の計算をする際に、配列(リスト※)に対してインデックスでアクセスして計算する方法ももちろんありますが、配列(リスト)の値に直接アクセスするような方法(for eachと思えばよい)で統一してみました。

ここでもいくつかの違いを比較してみましょう。

  • 型の宣言の有無(動的型付けと静的型付け)
  • 配列をループさせる方法
  • 二乗の計算方法(JavaとC#だけ二乗の計算で **2 ではなく、 (a-y)*(a-y)のような書き方)
  • distance配列への値の追加方法

型の宣言の違い

型付け 言語 型の宣言 コード量   学習コスト 開発規模 実行速度
動的 Ruby, Python, JavaScript 不要 少なめ 低め 小さめ 遅め
静的 Java, C# 必要 多め 高め 大きめ 速め

※ そういう傾向があるという程度の認識にしておきましょう。例外はたくさんあると思います。
型付けの特徴からもRubyなどの動的な型付けの言語のほうが初心者の方が学習をはじめるには向いているといえるかもしれません。

※ 実行速度の比較はこちら

配列へのfor eachを使用したアクセス

言語  配列のループ 説明
Java for (int[] coord : beaconCoordinates) coord にbeaconCoordinates内の値(y,x)が順番に格納される
Ruby beacon_coordinates.each do |coord|  coord にbeacon_coordinates内の値(y,x)が順番に格納される
Python for y, x in beacon_coordinates: y,xにbeacon_coordinates内の値が順番に格納される(y,xへの格納は同時)
JavaScript 入力の受け取りと同時に実行 (省略)
C# foreach (int[] coord in beaconCoordinates) coordにbeaconCoordinates内の値が順番に格納される

二乗の計算
せっかくなので累乗やルート(Math.Sqrtなど)の計算方法について説明します。実はJavaとC#以外は ** を使用して累乗を計算できるので ** 0.5 でルートの計算ができます。

言語 ルートの計算方法
Java Math.sqrt()
Ruby math.sqrt() または ** 0.5
Python math.sqrt() または ** 0.5
JavaScript Math.sqrt() または ** 0.5
C# Math.Sqrt()

distance配列への値の追加方法
(標準入力からの値の受け取りですでに登場していますが、ここで取り上げました)

言語 配列への値の追加
Java distances.add(distance)
Ruby distances << distance
Python distances.append(distance)
JavaScript distances.push(distance);
C# distances.Add(distance);

基本的には同じですね。特にJavaとC#はかなり似ています。そのほかの言語も追加に使用するキーワード(add,append,push)が違うだけで同じですね。Rubyだけ << という書き方になっています。これは矢印で追加しているイメージで、とても直感的ですね。ビット演算のビットシフト(桁をずらす操作)やC++の入出力(cin, cout)で同じような記号を使っているのを見かけます。

一番距離が短い地点の番号を出力

一番近い距離ではなく、一番近い距離の地点(インデックス)を出力するので次の2つのことが必要です。

  • 最小値を求める
  • 最小値を与えるインデックスを保持しておく

もしも、最小値を求めるだけであれば、例えばPythonでは次のようにすれば求めることができます。(distancesというリストに含まれる値の最小値が得られる)

min(distances)

各言語で配列(リスト)の最小値を求める方法

言語 最小値の求め方
Java Collections.min(distances)
Ruby distances.min
Python min(distances)
JavaScript Math.min(...distances)
C# distances.Min()

ここでも少し特徴がありますね。次の2つのパターンがありそうです。

最小値の求め方 言語
min(distances) Java, Python, JavaScript
distances.min Ruby, C#

おおむね次のように考えておけばだいたい合っているとおもいます。

  • min(distances) ・・・ 関数minの引数にdistancesを与えている
  • distances.min ・・・ distancesという配列をオブジェクト(あるクラスのインスタンス)として扱い、そのクラスのminメソッドを使用している。

わかりやすさ、書きやすさにはあまり違いはないように感じますね。


では、最小値を求める方法についてはこれくらいにして、最小値を求める部分と、最小値を与えるインデックスを求めて答えを出力する部分のコードを比較してみましょう。

  • Java
// 最小距離の探索
double minDistance = Double.MAX_VALUE;
int nearestBeaconIndex = -1;

for (int i = 0; i < distances.size(); i++) {
    double distance = distances.get(i);
    if (distance < minDistance) {
        minDistance = distance;
        nearestBeaconIndex = i;
    }
}
// 結果の出力
System.out.println(nearestBeaconIndex + 1);
  • Ruby
# 最小距離の探索
min_distance = Float::INFINITY
nearest_beacon_index = -1

distances.each_with_index do |distance, i|
  if distance < min_distance
    min_distance = distance
    nearest_beacon_index = i
  end
end
# 結果の出力
puts nearest_beacon_index + 1

distances配列に格納されている値(distance)とそのインデックス(i)を同時に取得しています!便利!

  • Python
# 最小距離の探索
min_distance = float('inf')
nearest_beacon_index = -1

for i, distance in enumerate(distances):
  if distance < min_distance:
    min_distance = distance
    nearest_beacon_index = i

# 結果の出力
print(nearest_beacon_index + 1)

Pythonもインデックスと配列(リスト)内の値を同時に取得することができるようですね.Rubyと同じですね.

  • JavaScript
// 最小距離の探索
let minDistance = Infinity;
let nearestBeaconIndex = -1;

distances.forEach((distance, i) => {
if (distance < minDistance) {
    minDistance = distance;
    nearestBeaconIndex = i;
}
});

// 結果の出力
console.log(nearestBeaconIndex + 1);

JavaScriptも値とインデックスと同時に取得していますね....

  • C#
// 最小距離の探索
double minDistance = double.MaxValue;
int nearestBeaconIndex = -1;

for (int i = 0; i < distances.Count; i++)
{
    double distance = distances[i];
    if (distance < minDistance)
    {
        minDistance = distance;
        nearestBeaconIndex = i;
    }
}

// 結果の出力
Console.WriteLine(nearestBeaconIndex + 1);

そこまで大きく差はない印象です。やや処理が複雑なので、基本的な処理の流れを記載しておきます。

  • 最小値を格納する変数(minDistance)をその型で扱える値の最大値で初期化
  • 最終的に出力する答え(nearestBeaconIndex)を-1で初期化
  • for ループを回して、minDistaceをどんどん更新していく。minDistanceを更新したインデックスの値でnearestBeaconIndexを更新する
  • nearestBeaconIndex+1の値(各言語は0から始まるインデックスで、地点番号と1ずれておりそれを合わせるために+1)を出力する

初期化部分の比較

言語 初期化(距離) 初期化(インデックス)
Java double minDistance = Double.MAX_VALUE; int nearestBeaconIndex = -1;
Ruby min_distance = Float::INFINITY nearest_beacon_index = -1
Python min_distance = float('inf') nearest_beacon_index = -1
JavaScript let minDistance = Infinity; let nearestBeaconIndex = -1;
C# double minDistance = double.MaxValue; int nearestBeaconIndex = -1;

いつだってJavaとC#は似ています。その2つとそれ以外で、最大値を表す言葉がMaxとInfinityというところも違っていて面白いですね。これも言語設計思想の違いによるものでしょうか?(わかりません)

ちなみに、最小値を求めるためにどんどん小さい値で更新していきたいので、初期値は大きくしておきます。
また、インデックスの初期化は答えとしてあり得ない値としておくことが多いです。これは(今回の問題ではありえませんが)条件を満たすようなものが存在しない場合と区別するためです。

一応これで今回の問題のコードを最後まで比較しました(すべての箇所を比較したわけではありませんが...)まとめとして各言語のコードの全文とレイミコメント(島根弁)でお示ししておきます。(Paizaで提出して正解できることを確認しています)

  • Java
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;

public class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 標準入力からN, a, bの値を受け取る
        int[] firstLine = new int[3];
        for (int i = 0; i < 3; i++) {
            firstLine[i] = scanner.nextInt();
        }
        int n = firstLine[0];
        int a = firstLine[1];
        int b = firstLine[2];

        // N個の地点の座標データを格納するためのリストを用意する
        List<int[]> beaconCoordinates = new ArrayList<>();

        // N個の地点の座標データを、標準入力から読み込み、リストに格納する
        for (int i = 0; i < n; i++) {
            int y = scanner.nextInt();
            int x = scanner.nextInt();
            beaconCoordinates.add(new int[]{y, x});
        }

        // 距離を格納する配列
        List<Double> distances = new ArrayList<>();

        // 各ビーコンとの距離を計算
        for (int[] coord : beaconCoordinates) {
            int y = coord[0];
            int x = coord[1];
            double distance = Math.sqrt((a - y) * (a - y) + (b - x) * (b - x));
            distances.add(distance);
        }

        // 最小距離の探索
        double minDistance = Double.MAX_VALUE;
        int nearestBeaconIndex = -1;

        for (int i = 0; i < distances.size(); i++) {
            double distance = distances.get(i);
            if (distance < minDistance) {
                minDistance = distance;
                nearestBeaconIndex = i;
            }
        }

        // 結果の出力
        System.out.println(nearestBeaconIndex + 1);

        scanner.close();
    }
}
  • Ruby
# 標準入力からN, a, bの値を受け取る
n, a, b = gets.chomp.split(" ").map(&:to_i)

# N個の地点の座標データを格納するための配列を用意する
beacon_coordinates = []

# N個の地点の座標データを、標準入力から読み込み、配列に格納する
n.times do
  y, x = gets.chomp.split(" ").map(&:to_i)
  beacon_coordinates << [y, x]
end

# 距離を格納する配列
distances = []

# 各ビーコンとの距離を計算
beacon_coordinates.each do |coord|
  y, x = coord[0], coord[1]
  distance = Math.sqrt((a - y)**2 + (b - x)**2)
  distances << distance
end

# 最小距離の探索
min_distance = Float::INFINITY
nearest_beacon_index = -1

distances.each_with_index do |distance, i|
  if distance < min_distance
    min_distance = distance
    nearest_beacon_index = i
  end
end

# 結果の出力
puts nearest_beacon_index + 1
  • Python
import math

# 標準入力からN, a, bの値を受け取る
n, a, b = map(int, input().split())

# N個の地点の座標データを格納するためのリストを用意する
beacon_coordinates = []

# N個の地点の座標データを、標準入力から読み込み、リストに格納する
for _ in range(n):
  y, x = map(int, input().split())
  beacon_coordinates.append([y, x])

# 距離を格納するリスト
distances = []

# 各ビーコンとの距離を計算
for y, x in beacon_coordinates:
  distance = math.sqrt((a - y)**2 + (b - x)**2)
  distances.append(distance)

# 最小距離の探索
min_distance = float('inf')
nearest_beacon_index = -1

for i, distance in enumerate(distances):
  if distance < min_distance:
    min_distance = distance
    nearest_beacon_index = i

# 結果の出力
print(nearest_beacon_index + 1)
  • JavaScript
const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout,
});

let n, a, b;
let beaconCoordinates = [];
let distances = [];
let count = 0; // カウンタをグローバルスコープに移動

readline.on('line', line => {
  count++;
  if (count === 1) {
    [n, a, b] = line.split(" ").map(Number);
  } else if (count <= n + 1) {
    const [y, x] = line.split(" ").map(Number);
    beaconCoordinates.push([y, x]);
  }

  if (count === n + 1) {
    // 各ビーコンとの距離を計算
    beaconCoordinates.forEach(([y, x]) => {
      const distance = Math.sqrt((a - y) ** 2 + (b - x) ** 2);
      distances.push(distance);
    });

    // 最小距離の探索
    let minDistance = Infinity;
    let nearestBeaconIndex = -1;

    distances.forEach((distance, i) => {
      if (distance < minDistance) {
        minDistance = distance;
        nearestBeaconIndex = i;
      }
    });

    // 結果の出力
    console.log(nearestBeaconIndex + 1);
    readline.close();
  }
});
  • C#
using System;
using System.Collections.Generic;
using System.Linq;

public class NearestBeacon
{
    public static void Main(string[] args)
    {
        // 標準入力からN, a, bの値を受け取る
        int[] firstLine = Console.ReadLine().Split(" ").Select(int.Parse).ToArray();
        int n = firstLine[0];
        int a = firstLine[1];
        int b = firstLine[2];

        // N個の地点の座標データを格納するためのリストを用意する
        List<int[]> beaconCoordinates = new List<int[]>();

        // N個の地点の座標データを、標準入力から読み込み、リストに格納する
        for (int i = 0; i < n; i++)
        {
            int[] coordinates = Console.ReadLine().Split(" ").Select(int.Parse).ToArray();
            int y = coordinates[0];
            int x = coordinates[1];
            beaconCoordinates.Add(new int[] { y, x });
        }

        // 距離を格納する配列
        List<double> distances = new List<double>();

        // 各ビーコンとの距離を計算
        foreach (int[] coord in beaconCoordinates)
        {
            int y = coord[0];
            int x = coord[1];
            double distance = Math.Sqrt((a - y) * (a - y) + (b - x) * (b - x));
            distances.Add(distance);
        }

        // 最小距離の探索
        double minDistance = double.MaxValue;
        int nearestBeaconIndex = -1;

        for (int i = 0; i < distances.Count; i++)
        {
            double distance = distances[i];
            if (distance < minDistance)
            {
                minDistance = distance;
                nearestBeaconIndex = i;
            }
        }

        // 結果の出力
        Console.WriteLine(nearestBeaconIndex + 1);
    }
}

最後の仕上げ

 以上ですが、レイミコメントでつぎのようなことを言われたので、最後にそれを反映させたRubyによる実装をお示したいと思います。

  • 距離はルートじゃなくて、二乗の値をそのままつかってもよい(最初にも書きました)
  • distances配列を使わなくても良い
# 標準入力からN, a, bの値を受け取る
n, a, b = gets.chomp.split(" ").map(&:to_i)

# 最小距離を初期化
min_distance = Float::INFINITY
nearest_beacon_index = -1

# 各ビーコンとの距離を計算し、最小距離を更新
n.times do |i|
 y, x = gets.chomp.split(" ").map(&:to_i)
 distance = (a - y)**2 + (b - x)**2
 if distance < min_distance
   min_distance = distance
   nearest_beacon_index = i
 end
end

# 結果を出力
puts nearest_beacon_index + 1

入力で受け取った座標を受け取った瞬間に距離を計算するという形にすれば、distances配列に格納する必要がなくなります。
forループも1つに減りました。(今回の場合はそもそも処理がシンプルで軽いため実行時間に変化はありませんでした)

(別解)Rubyの機能をうまく使うとループ処理の部分をさらに簡潔に記述することができます。

# 現在地とビーコンの数を取得
n, a, b = gets.split.map(&:to_i)

# ビーコンの位置を読み込み、現在地からの距離を計算
distances = n.times.map do |i|
 y, x = gets.split.map(&:to_i)
 (a - y)**2 + (b - x)**2  # 距離の二乗を計算
end

# 最小の距離を持つビーコンのインデックスを求め、結果を出力
puts distances.each_with_index.min[1] + 1

 ちなみに記事のここまでの内容でRubyと似ている部分が多かったPythonで同じような書き方にしようとすると次のようになります。

n, a, b = map(int, input().split())

distances = [(a - y)**2 + (b - x)**2 for y, x in [map(int, input().split()) for _ in range(n)]]

min_index = distances.index(min(distances))

print(min_index + 1)

 Pythonのほうがコードとしては短いですが、やや可読性が落ちるように感じます(個人差があります)。どちらもループを回して距離の値をdistances配列に格納し、最後に最小値を与えるインデックスを出力しているので処理としては同じですが、Rubyのほうがコードとやっていることが結びつきやすいように思います。
 そういった意味でRubyは初心者の方が学習するのに適しているといえるのかもしれません(ほかの言語が初心者の方の学習に適していないとは言っていません。)

速度比較

実行時間の違いの傾向について触れたので、各言語の実行速度を比較してみます。

条件

  • 0から100000000(1億)まで順番に加算していく(5回実行した平均)
  • 実行環境はPaiza.io

速度比較用プログラム

  • Java
public class Main {
    public static void main(String[] args) {
        int iterations = 100000000; // 1億回
        int numRuns = 5; // 実行回数
        double[] durations = new double[numRuns];
        double totalDuration = 0;

        for (int run = 0; run < numRuns; run++) {
            long startTime = System.nanoTime();
            double sum = 0;
            for (int i = 0; i < iterations; i++) {
                sum += i;
            }
            long endTime = System.nanoTime();
            double duration = (endTime - startTime) / 1000000.0; // ミリ秒に変換
            durations[run] = duration;
            totalDuration += duration;
            System.out.println("Java: Run " + (run + 1) + ", Sum = " + sum + ", Time = " + duration + " ms");
        }

        double averageDuration = totalDuration / numRuns;
        System.out.println("Java: Average Time = " + averageDuration + " ms");
    }
}

Java: Run 1, Sum = 4.99999995E15, Time = 79.688545 ms
Java: Run 2, Sum = 4.99999995E15, Time = 81.279807 ms
Java: Run 3, Sum = 4.99999995E15, Time = 77.061108 ms
Java: Run 4, Sum = 4.99999995E15, Time = 77.052876 ms
Java: Run 5, Sum = 4.99999995E15, Time = 77.072958 ms
Java: Average Time = 78.4310588 ms

  • Ruby
iterations = 1_000_000 # 100万回
num_runs = 5 # 実行回数
durations = []
total_duration = 0

num_runs.times do |run|
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  sum = 0
  iterations.times do |i|
    sum += i
  end
  end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  duration = (end_time - start_time) * 1000 # ミリ秒に変換
  durations << duration
  total_duration += duration
  puts "Ruby: Run #{run + 1}, Sum = #{sum}, Time = #{duration} ms"
end

average_duration = total_duration / num_runs
puts "Ruby: Average Time = #{average_duration} ms"

Ruby: Run 1, Sum = 499999500000, Time = 39.159736945293844 ms
Ruby: Run 2, Sum = 499999500000, Time = 39.18685589451343 ms
Ruby: Run 3, Sum = 499999500000, Time = 39.19621405657381 ms
Ruby: Run 4, Sum = 499999500000, Time = 39.223307045176625 ms
Ruby: Run 5, Sum = 499999500000, Time = 39.23868900164962 ms
Ruby: Average Time = 39.200960588641465 ms

Paiza.ioでは1億回のループはTimeOutになってしまったので,100万回のループの計測結果

  • Python
import time

iterations = 1000000 # 100万回
num_runs = 5 # 実行回数
durations = []
total_duration = 0

for run in range(num_runs):
    start_time = time.time()
    sum = 0
    for i in range(iterations):
        sum += i
    end_time = time.time()
    duration = (end_time - start_time) * 1000 # ミリ秒に変換
    durations.append(duration)
    total_duration += duration
    print(f"Python: Run {run + 1}, Sum = {sum}, Time = {duration} ms")

average_duration = total_duration / num_runs
print(f"Python: Average Time = {average_duration} ms")

Python: Run 1, Sum = 499999500000, Time = 79.61845397949219 ms
Python: Run 2, Sum = 499999500000, Time = 79.77485656738281 ms
Python: Run 3, Sum = 499999500000, Time = 78.86457443237305 ms
Python: Run 4, Sum = 499999500000, Time = 79.80942726135254 ms
Python: Run 5, Sum = 499999500000, Time = 81.76827430725098 ms
Python: Average Time = 79.96711730957031 ms

PythonもRubyと同じくPaiza.ioでは1億回のループはTimeOutになってしまったので,100万回のループの計測結果

  • JavaScript
const iterations = 100000000; // 1億回
const numRuns = 5; // 実行回数
let durations = [];
let totalDuration = 0;

for (let run = 0; run < numRuns; run++) {
    const startTime = process.hrtime.bigint();
    let sum = 0;
    for (let i = 0; i < iterations; i++) {
        sum += i;
    }
    const endTime = process.hrtime.bigint();
    const duration = Number(endTime - startTime) / 1000000; // ミリ秒に変換
    durations.push(duration);
    totalDuration += duration;
    console.log(`JavaScript: Run ${run + 1}, Sum = ${sum}, Time = ${duration} ms`);
}

const averageDuration = totalDuration / numRuns;
console.log(`JavaScript: Average Time = ${averageDuration} ms`);

JavaScript: Run 1, Sum = 4999999950000000, Time = 80.217823 ms
JavaScript: Run 2, Sum = 4999999950000000, Time = 80.911678 ms
JavaScript: Run 3, Sum = 4999999950000000, Time = 80.909171 ms
JavaScript: Run 4, Sum = 4999999950000000, Time = 77.048423 ms
JavaScript: Run 5, Sum = 4999999950000000, Time = 77.087661 ms
JavaScript: Average Time = 79.2349512 ms

  • C#
using System;
using System.Diagnostics;

public class PerformanceTest {
    public static void Main(string[] args) {
        int iterations = 100000000; // 1億回
        int numRuns = 5; // 実行回数
        double[] durations = new double[numRuns];
        double totalDuration = 0;

        for (int run = 0; run < numRuns; run++) {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            double sum = 0;
            for (int i = 0; i < iterations; i++) {
                sum += i;
            }
            stopwatch.Stop();
            double duration = stopwatch.ElapsedMilliseconds;
            durations[run] = duration;
            totalDuration += duration;
            Console.WriteLine("C#: Run " + (run + 1) + ", Sum = " + sum + ", Time = " + duration + " ms");
        }

        double averageDuration = totalDuration / numRuns;
        Console.WriteLine("C#: Average Time = " + averageDuration + " ms");
    }
}

C#: Run 1, Sum = 4.99999995E+15, Time = 365 ms
C#: Run 2, Sum = 4.99999995E+15, Time = 365 ms
C#: Run 3, Sum = 4.99999995E+15, Time = 365 ms
C#: Run 4, Sum = 4.99999995E+15, Time = 365 ms
C#: Run 5, Sum = 4.99999995E+15, Time = 365 ms
C#: Average Time = 365 ms

速度比較まとめ

言語 Average Time(ms)
Java 78.43
Ruby 39.20 ※
Python 79.97 ※
JavaScript 79.23
C# 365

※ Java,JavaScript,C#は1億回のループ、Ruby、Pythonは100万回のループ

思った以上に差が出る結果となりました。コードが複雑な一見とっつきにくそうなものでもきちんとこのようなメリットがあるようですね。

この結果はあくまでも私の環境(Paiza.ioですが)の,上記のコードによる結果です.さまざまな要因で異なる結果となる可能性はありますが,基本的な実行速度にはやはり違いがあると認識していてもよいと思います。

おわりに

 なんだかとても長い記事になってしまいました。ここまで読んでくださった方、途中飛ばしたけど、ここに目を留めてくださった方、ありがとうございます。
Ruby推しということで記事を書きましたが、私は普段は主にPythonを使用してPaiza(やその他のこと)に取り組んでいます。

 また、今回取り上げたJava,JavaScript,C#も書いたことはありますが、普段から使っているというわけではないのでコードとして不適切な部分があるかもしれません。もしお気づきの点があればコメント等でご指摘いただければと思います。

 途中でも記載しましたが、各言語には設計思想というか、特徴があり、どの言語が優れているということが簡単にいえるものではありません。(業務で使う場合なんて、その言語を選定した理由は、既存システムがその言語で作られていたから、顧客からのオーダーだから、という理由で言語が決定する場合もあると思います)

 ですので、これから勉強を始める方はなんでもいいから自分が気に入った言語で勉強を始めてみるということが一番だと思います。そのときにこの記事が言語を選ぶ参考になれば幸いです。

おまけ(島根弁について)

 私は正真正銘の島根出身者(就活でNaClの会社見学にも行きました.)ですが、レイミ(島根弁)のような喋り方はあまりしません。

島根弁は東西で2つに分かれているという認識で、東が出雲弁(レイミはおそらくこれ)と西が石見(いわみ)弁です。私は石見の出身なので少しだけ違和感があります。

例えば、「〜だに」は使いませんが、そのかわりに「〜だけ」と言ったりします。「なっちょる」、「ごしない」は言います。

最後に標準語と島根弁(出雲、石見)を比較してこの記事を締めくくります。

標準語 出雲弁 石見弁
あれ あげ あが
これ こげ こが
それ そげ そが
どれ どげ どが
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?