質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

7回答

5228閲覧

数字が斜めに増えていく正方配列の生成(アルゴリズム)

t-miyazaki

総合スコア71

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

3クリップ

投稿2015/11/22 15:55

編集2015/11/23 15:32

正の整数nが与えられたとき、次のようなn * nの二次元配列を生成する関数を作成してください。

  1. 一番左上の値は 1 です。
  2. 左下方向に行くに従って値が1ずつ増えていきます。
  3. これ以上左下方向に行けなくなったら、右上の次の位置に戻ります。

例えば、n = 4が与えられたとき、

1 2 4 7 3 5 8 11 6 9 12 14 10 13 15 16

を生成します。

プログラム言語は何でも構いません。命令型でも関数型でも大丈夫です。


解決後追記:

より良い回答を頂いた後に何なんですが、念のため昨日質問をする前に私が書いたコードを載せておきます。

Java

1 public static int[][] makeArray(int n) { 2 int[][] array = new int[n][n]; 3 4 int num = 1; 5 int istart = 0, jstart = 0; 6 int i = 0, j = 0; 7 8 while (true) { 9 array[i][j] = num; 10 num++; 11 12 // リセットの場合 13 if (i == jstart && j == istart) { 14 // 終了の場合 15 if (i == n - 1 && j == n - 1) 16 break; 17 18 if (istart != n - 1) { 19 istart++; 20 } else { 21 jstart++; 22 } 23 i = istart; 24 j = jstart; 25 continue; 26 } 27 // 続ける場合 28 i--; 29 j++; 30 } 31 return array; 32 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答7

0

Javascript

1function hoge ( n ) { 2 var ary = []; 3 var i; 4 5 for ( i = 0; i < n; i ++ ) { 6 ary[ i ] = []; 7 } 8 9 fuga( ary, 0, 0, 1, n ); 10 moga( ary, n - 1, n - 1, n * n, n ); 11 12 function fuga( ary, i, j, x, n ) { 13 if ( j == n ) return; 14 15 ary[ i ][ j ] = x; 16 17 if ( j == 0 ) { 18 fuga( ary, j, i + 1, x + 1, n ); 19 } else { 20 fuga( ary, i + 1, j - 1, x + 1, n ); 21 } 22 } 23 24 function moga( ary, i, j, x, n ) { 25 if ( i == n - 1 && j == 0 ) return; 26 27 ary[ i ][ j ] = x; 28 29 if ( j == n - 1 ) { 30 moga( ary, j, i - 1, x - 1, n ) 31 } else { 32 moga( ary, i - 1, j + 1, x - 1, n ); 33 } 34 } 35 36 return ary; 37} 38 39console.log( hoge( 4 ) );

あまりきれいなプログラムじゃありませんが

Javascript

1function hoge ( n ) { 2 var ary = []; 3 var i; 4 5 for ( i = 0; i < n; i ++ ) { 6 ary[ i ] = []; 7 } 8 9 moga( ary, 0, 0, 1, 1, n ); 10 11 function fuga( ary, i, j, x, y, n ) { 12 if ( y < 0 || j < 0 ) return; 13 14 if ( i < n && j < n ) { 15 ary[ i ][ j ] = x; 16 } 17 if ( i == 0 ) { 18 fuga( ary, i + 1, j - 1, x + 1, 0, n ); 19 x += y; 20 if ( j < n - 2 ) { 21 y++; 22 } else if ( j > n - 2 ) { 23 y--; 24 } 25 fuga( ary, i, j + 1, x, y, n ); 26 } else { 27 fuga( ary, i + 1, j - 1, x + 1, y, n ); 28 } 29 } 30 31 return ary; 32} 33 34console.log( hoge( 4 ) );

Javascript

1function hoge( n ) { 2 var ary = []; 3 var i, j, k, x = 1; 4 5 for ( i = 0; i < n; i ++ ) ary[ i ] = []; 6 7 for ( k = 0; k <= ( n - 1 ) * 2; k ++ ) 8 for ( i = 0, j = k; j >= 0; i ++, j -- ) 9 if ( i < n && j < n ) 10 ary[ i ][ j ] = x ++; 11 12 return ary; 13} 14console.log( hoge( 4 ) );

最短で4行、これ以上無理

投稿2015/11/22 18:59

編集2015/11/23 04:39
yuux01

総合スコア34

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/23 04:52

最短4行の案、スマートで良いですね! 正方形をカバーする大きな三角形を描いて、正方形内のみ描画するって感じですね。
yuux01

2015/11/23 06:56

9つの点を通る一筆書きを思い出したんです。 ただ、はみ出す分、無駄な処理がありますが。
yuux01

2015/11/23 07:49 編集

for( i = 0; i < n; i++ ) for( j = 0; j <= i; j++ ) { ary[ j ][ i - j ] = x ++; ary[ n - j - 1 ][ n - i + j - 1 ] = n * n + 1 - ary[ j ][ i - j ]; } catsforepaw さんのをお借りして、4.5行プログラムw 若干無駄が少なくなってます。 数列の和を参考にしてみました。
t-miyazaki

2015/11/23 15:26

回答ありがとうございます。ベストアンサーの方のところに総括してコメントを 入れさせていただきました。 最後の4行のプログラム、こちらもかなりまとまっていて良いですね。
guest

0

こんなところでしょうか。

C++

1#include <cstdio> 2#include <iostream> 3#include <algorithm> 4#include <vector> 5 6std::vector<std::vector<int>> generate(int n) 7{ 8 std::vector<std::vector<int>> data(n); 9 std::fill(data.begin(), data.end(), std::vector<int>(n)); 10 11 // ◤の部分 12 int a = 1; 13 for(int i = 0; i < n; i++) 14 { 15 for(int j = 0; j <= i; j++) 16 { 17 data[j][i - j] = a++; 18 } 19 } 20 // ◢の部分 21 a = n * n; 22 for(int i = 0; i < n - 1; i++) 23 { 24 for(int j = 0; j <= i; j++) 25 { 26 data[(n - 1) - j][(n - 1) - (i - j)] = a--; 27 } 28 } 29 return data; 30} 31 32int main() 33{ 34 for(;;) 35 { 36 int n; 37 std::cout << "n (0=exit) ? "; 38 std::cin >> n; 39 if(n == 0) 40 break; 41 42 auto data = generate(n); 43 for(int i = 0; i < n; i++) 44 { 45 for(int j = 0; j < n; j++) 46 { 47 printf(" %4d", data[i][j]); 48 } 49 std::cout << std::endl; 50 } 51 std::cout << std::endl; 52 } 53 return 0; 54}

平行四辺形にすることに気づいたらコードが単純化できました。

投稿2015/11/22 21:10

編集2015/11/23 00:48
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t-miyazaki

2015/11/23 15:24

回答ありがとうございます。ベストアンサーの方のところに総括してコメントを 入れさせていただきました。
guest

0

yuux01 さんの解を ruby でかいてみました。

斜め線でスキャンをしていき、正方形領域にひっかかったときだけ
配列に値を設定していきます。
(x < n && y < n が正方形の内部か? の判定になります。
この判定部分を変更すると、三角形や円の形で切り取ることができるようになります。)

ruby

1def gen_square(n) 2 ary = Array.new(n).map! { Array.new(n, 0) } 3 seq = 1 4 (0..n * 2 - 1).each do |scan_line| 5 (0..scan_line).each do |y| 6 x = scan_line - y 7 if x < n && y < n 8 ary[y][x] = seq 9 seq += 1 10 end 11 end 12 end 13 ary 14end 15 16def show_square(ary) 17 return if ary.size == 0 18 keta = Math.log10(ary.size * ary.size).floor + 2 19 fmt = "%#{keta}d" 20 ary.each do |line| 21 s = '' 22 line.each do |item| 23 s += format(fmt, item) 24 end 25 puts s 26 end 27end 28 29[0, 1, 2, 3, 4, 10].each do |d| 30 ary = gen_square(d) 31 show_square(ary) 32 puts 33end

実行例

1 1 2 3 4 1 2 4 3 5 7 6 8 9 1 2 4 7 3 5 8 11 6 9 12 14 10 13 15 16 1 2 4 7 11 16 22 29 37 46 3 5 8 12 17 23 30 38 47 56 6 9 13 18 24 31 39 48 57 65 10 14 19 25 32 40 49 58 66 73 15 20 26 33 41 50 59 67 74 80 21 27 34 42 51 60 68 75 81 86 28 35 43 52 61 69 76 82 87 91 36 44 53 62 70 77 83 88 92 95 45 54 63 71 78 84 89 93 96 98 55 64 72 79 85 90 94 97 99 100

投稿2015/11/23 07:36

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

ary[y][x] の値を求める関数を func(x, y, n) としてくくりだして、
2 次元配列に値を設定するようにしました。

ary[0][x] (四角の上辺の値)は (x * x + x) / 2 + 1 で計算できること、
ary[n-1][n-1-x] (四角の下辺の値)は n * n - (y * y + y) / 2 で計算できること、
を使って、 それ以外の ary[y][x] の値を計算しています。

四角の上辺の値 1, 2, 4, 7, 11, ... の数列は
隣同士の差が 1, 2, 3, 4, ... となっています。

ruby

1def sub(x) 2 (x * x + x) / 2 3end 4 5def func(x, y, n) 6 if x + y < n 7 sub(x + y) + 1 + y 8 else 9 n * n - sub(2 * n - 1 - x - y) + n - x 10 end 11end 12 13def gen_square(n) 14 ary = Array.new(n).map! { Array.new(n, 0) } 15 (0..n - 1).each do |y| 16 (0..n - 1).each do |x| 17 ary[y][x] = func(x, y, n) 18 end 19 end 20 ary 21end 22 23def show_square(ary) 24 return if ary.size == 0 25 keta = Math.log10(ary.size * ary.size).floor + 2 26 fmt = "%#{keta}d" 27 ary.each do |line| 28 s = '' 29 line.each do |item| 30 s += format(fmt, item) 31 end 32 puts s 33 end 34end 35 36[0, 1, 2, 3, 10].each do |d| 37 ary = gen_square(d) 38 show_square(ary) 39 puts 40end

実行結果

1 1 2 3 4 1 2 4 3 5 7 6 8 9 1 2 4 7 11 16 22 29 37 46 3 5 8 12 17 23 30 38 47 56 6 9 13 18 24 31 39 48 57 65 10 14 19 25 32 40 49 58 66 73 15 20 26 33 41 50 59 67 74 80 21 27 34 42 51 60 68 75 81 86 28 35 43 52 61 69 76 82 87 91 36 44 53 62 70 77 83 88 92 95 45 54 63 71 78 84 89 93 96 98 55 64 72 79 85 90 94 97 99 100

追記:
v -> Ux,y) を求める方法は次のようになります。

ruby

1def pos(v, n) 2 if v <= (n * n + 1) / 2 3 pos_0(v, n) 4 else 5 x, y = pos_0(n * n + 1 - v, n) 6 [n - 1 - x, n - 1 - y] 7 end 8end 9 10def pos_0(v, n) 11 z = 1 # 四角の上辺の数列の初期値 12 d = 1 # 四角の上辺の数列の次の項との差分 13 tx = 0 # 四角の上辺の位置 14 if v <= (n * n + 1) / 2 15 while z <= v 16 z += d 17 d += 1 18 tx += 1 19 end 20 z -= (d - 1) 21 x = tx - (v - z) - 1 22 y = v - z 23 [x, y] 24 else 25 fail "Bad argument v:#{v}, n:#{n}" 26 end 27end 28 29def gen_square(n) 30 ary = Array.new(n).map! { Array.new(n, 0) } 31 (1..n * n).each do |v| 32 x, y = pos(v, n) 33 ary[y][x] = v 34 end 35 ary 36end

投稿2015/11/23 03:35

編集2015/11/23 07:34
katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

katoy

2015/11/23 03:46

上では (x, y) -> v (配列の位置から埋めるべきを求める) で配列を埋めていきましたが、 逆に v -> (x, y) (値 v が現れる 配列の位置を求める) を使って配列を埋めていくという解法も考えられます。
t-miyazaki

2015/11/23 15:23

ご回答いただき、どうもありがとうございました。 もともとは、このような正方行列の行列式を計算したかったのですが、 任意のnにおける正方行列の生成を自分ではエレガントに書けませんでしたので 質問させていただいた次第です。 規則的な配列でもスマートに書こうと思うとなかなか難しいこと、 人によっても実装の仕方がけっこう変わってくることが面白く思いました。 理想的には、位置(x,y)における要素の値を算出できる一般式を用いて書くと すっきり書けると思います。 今回は、そのような回答を与えてくださったkatoyさんの回答をベストアンサーとさせて いただきたいと思います。 重ね重ね、ありがとうございました。
katoy

2015/11/23 15:43

この質問については、結局 4つの方針の解を書いてしまいました。 1. (x, y) -> v の関数を作って、配列を埋める。 2. v -> (x, y) の関数を作って、配列を埋める。 3. 配列をたどる手順をそのままコーディングする 4. Scan_line で該当図形とのマスクをとる。 さらに他の発想による解もあると思っています。
guest

0

2重ループ1つでなんとかなりました。

yは各行の先頭の値です。
横へ移動した時の増分をコントロールして各要素に入れる数値xを生成しています。
左上△エリアと右下△エリアで増分の計算式が変わっています。

C++

1#include <iostream> 2#include <iomanip> 3#include <vector> 4#include <cstdlib> 5#include <climits> 6 7typedef std::vector<std::vector<int> > Array2D; 8 9Array2D createArray(size_t n) 10{ 11 Array2D wArray2D(n, std::vector<int>(n)); 12 13 int y=1; 14 for (int j=0; j < n; ++j) { 15 int x=y; 16 for (int i=0; i < n; ++i) { 17 wArray2D[i][j]=x; 18 if (i < (n-j-1)) 19 x += (j+i+1); 20 else 21 x += (n-j-1 + n-i-1); 22 } 23 y += (j+2); 24 } 25 26 return wArray2D; 27} 28 29int main(int argc, char* argv[]) 30{ 31 if (argc < 2) 32return 1; 33 34 size_t n=strtoul(argv[1], 0, 10); 35 if ((n == 0) || (n == ULONG_MAX)) 36return 1; 37 38 Array2D wArray2D = createArray(n); 39 for (int j=0; j < n; ++j) { 40 for (int i=0; i < n; ++i) { 41 std::cout << std::setw(4) << wArray2D[i][j]; 42 } 43 std::cout << "\n"; 44 } 45 46 return 0; 47}

2重配列をコピーで返却しているので、本当はstd::move()を使って返却した方がよいです。
std::move()はC++11以上でないと使えないので、ここでは見送りました。。


【閑話休題】
しかし、こんなに短いプログラムでも、C++11の恩恵が3箇所もありました。(nullptr, テンプレート・パラメータの閉じ括弧>>, std::move)
C++11って偉大です。

投稿2015/11/23 01:22

編集2015/11/23 04:35
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t-miyazaki

2015/11/23 15:24

回答ありがとうございます。ベストアンサーの方のところに総括してコメントを 入れさせていただきました。
guest

0

質問文の手順をそのままコードにしてみました。

ruby

1# See https://teratail.com/questions/20823 2def gen_square(n) 3 ary = Array.new(n).map! { Array.new(n, 0) } 4 seq = 1 5 (0..n - 1).each do |x| 6 (0..x).each do |i| 7 ary[i][x - i] = seq 8 seq += 1 9 end 10 end 11 12 (1..n - 1).each do |y| 13 (0..n - 1 - y).each do |i| 14 ary[y + i][n - 1 - i] = seq 15 seq += 1 16 end 17 end 18 ary 19end 20 21def show_square(ary) 22 return if ary.size == 0 23 keta = Math.log10(ary.size * ary.size).floor + 2 24 fmt = "%#{keta}d" 25 ary.each do |line| 26 s = '' 27 line.each do |item| 28 s += format(fmt, item) 29 end 30 puts s 31 end 32end 33 34[0, 1, 2, 3, 10].each do |d| 35 ary = gen_square(d) 36 show_square(ary) 37 puts 38end

実行例:

$ ruby 1.rb 1 1 2 3 4 1 2 4 3 5 7 6 8 9 1 2 4 7 11 16 22 29 37 46 3 5 8 12 17 23 30 38 47 56 6 9 13 18 24 31 39 48 57 65 10 14 19 25 32 40 49 58 66 73 15 20 26 33 41 50 59 67 74 80 21 27 34 42 51 60 68 75 81 86 28 35 43 52 61 69 76 82 87 91 36 44 53 62 70 77 83 88 92 95 45 54 63 71 78 84 89 93 96 98 55 64 72 79 85 90 94 97 99 100

ary[i]j] を計算で求めることができれば、単純なコードにできます。
( 九九の表なら ary[y][x] = (y+1) * (x+1) を単純に代入していくだけですむように)
□の左上と右下の三角形にわければ、計算式が書ける気がするんですが...

投稿2015/11/23 00:01

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

少しコードが汚い気もするのですが,こんな感じでどうでしょう?
左上から始めて添え字を左下方向に動かしていきます.(x -= 1,y += 1)
x が左端を超える場合やyが下の端を超える場合(x == 0 or y+1 == size)は
正しい位置に移動するように添え字を代入します.
左上半分を埋めている時は,単純にx = y+1,y = 0で十分です.
右下半分に差し掛かった時は,直前のyとxの位置に合わせてyの位置を工夫する必要があります.

python

1#!/usr/bin/python 2import numpy as np 3size = int(input()) 4array = np.zeros([size, size]) 5 6x = 0 7y = 0 8for i in range(size*size): 9 array[y][x] = i+1 10 if x == 0 or y+1 == size: 11 tx = x 12 x = y+1 if (y+1 < size) else size-1 13 y = 0 if (y+1 < size and tx == 0) else (y+1)+(tx+1)-size 14 else: 15 x -= 1 16 y += 1 17 18print array

配列の添え字が0から始まる言語と1から始まる言語とでは書き方が若干変わると思いますが,
1から始まる言語では適宜xとyを変更してください.

投稿2015/11/22 18:26

KenTerada

総合スコア751

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t-miyazaki

2015/11/23 15:26

回答ありがとうございます。ベストアンサーの方のところに総括してコメントを 入れさせていただきました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問