2015-07-22 2 views
4

Un groupe de gars et moi-même du travail essayions de comprendre ce problème, mais ne trouvions pas de méthode mathématique propre et efficace pour résoudre ce problème. Le voici:Placer des carrés sur un plan cartésien et élaborer leurs coordonnées X, Y sur la base du numéro de séquence

Etant donné un plan cartésien standard et une séquence d'images (toutes carrées et de mêmes dimensions, on dira juste 1 unité par exemple) comment les organisons-nous de façon à ce qu'elles tournent en spirale l'origine (0,0) de l'avion par leur coin supérieur gauche. Mais aussi plus spécifiquement, si l'on donne le nombre 25 par exemple (le 25ème carré de la séquence), que seraient les coordonnées X, Y?

enter image description here

Espérons que cette image brute aide à expliquer la séquence. Le tout premier carré à placer sur la grille serait le rouge, puis le bleu, le jaune, le violet, le vert, le noir, le brun, et ainsi de suite, comme l'illustrent les points. Nous espérions qu'il existait une formule mathématique «relativement» simple pour cela, mais c'est peut-être un vœu pieux.

+0

Vous devriez indiquer l'ordre. Je suppose que tous les carrés sont la première spirale autour de l'origine et les points sont les seconds? –

+0

Que voulez-vous dire par les coordonnées? Centre de carré? Coin gauche? Par exemple - quel numéro attribueriez-vous au carré noir? (2, -2)? –

+0

@ r3oath Deux commentaires: 1) Vous devez étiqueter cette question avec le langage de programmation dans lequel vous envisagez de mettre en œuvre votre solution, et 2) vous devriez revoir les réponses bien réfléchies données ci-dessous. –

Répondre

1

(Sur edit: j'ai ajouté une deuxième fonction qui permet la version de coordonnées cartésiennes à obtenir directement.)

Je suis arrivé ce bien avant ma tête a explosé. C'est une forme fermée dans le sens où elle donne les coordonnées de dire le millionième carré sans avoir besoin de les placer une par une en boucle. L'écrire ne donnerait pas une bonne formule mais vous pourriez écrire comme une formule définie par morceaux avec 8 pièces. La réponse est donnée en coordonnées de grille basées sur 1. La première coordonnée vous indique combien de cases vous êtes à droite ou à gauche de l'axe des ordonnées et la seconde coordonnée à quelle distance vers le haut ou vers le bas. A partir de ces chiffres, il est relativement facile, par ex. obtenir les coordonnées cartésiennes de leur coin supérieur gauche. J'implémenté dans le python:

from math import sqrt, ceil 

def coordinates(m): 
    n = ceil(sqrt(m)/2) #shell number 
    i = m - 4*(n-1)**2 #index in shell 
    if i <= n: 
     return (n,-i) 
    elif i <= 2*n-1: 
     return (2*n - i, -n) 
    elif i <= 3*n - 1: 
     return (2*n - 1 - i, -n) 
    elif i <= 4*n - 2: 
     return (-n, -4*n + 1 + i) 
    elif i <= 5*n - 2: 
     return (-n, -4*n + 2 + i) 
    elif i <= 6*n - 3: 
     return (-6*n + 2 + i, n) 
    elif i <= 7*n - 3: 
     return (-6*n + 3 + i,n) 
    else: 
     return (n, 8*n -3 - i) 

Pour obtenir le (x, y) les coordonnées cartésiennes des coins supérieurs gauche de la (i, j) coordonnées de grille vous pouvez utiliser la fonction suivante, qui a un paramètre de largeur en option pour permettre carrés non unitaires:

def cartesianFromGrid(i,j,w = 1): 
    x = w * (i if i < 0 else i - 1) 
    y = w * (j if j > 0 else j + 1) 
    return (x,y) 

Il est possible d'obtenir directement les coordonnées cartésiennes du coin supérieur gauche directement sans passer d'abord par coordonnées de la grille. La formule résultante implique moins de cas (puisque je n'ai pas besoin de passer de 1 directement à -1 ou vice versa) bien que je garde les deux formules dans la réponse puisque la perspective de la grille est plus naturelle:

def cartCoordinates(m): 
    n = ceil(sqrt(m)/2) #shell number 
    i = m - 4*(n-1)**2 #index in shell 
    if i <= n: 
     return (n-1,-i+1) 
    elif i <= 3*n - 1: 
     return (2*n - 1 - i, -n + 1) 
    elif i <= (5*n - 2): 
     return (-n, -4*n + 2 + i) 
    elif i <= 7*n - 3: 
     return (-6*n + 2 + i, n) 
    else: 
     return (n-1, 8 * n - 3 - i) 
def cartCoordinates(m): 
    n = ceil(sqrt(m)/2) #shell number 
    i = m - 4*(n-1)**2 #index in shell 
    if i <= n: 
     return (n-1,-i+1) 
    elif i <= 3*n - 1: 
     return (2*n - 1 - i, -n + 1) 
    elif i <= (5*n - 2): 
     return (-n, -4*n + 2 + i) 
    elif i <= 7*n - 3: 
     return (-6*n + 2 + i, n) 
    else: 
     return (n-1, 8 * n - 3 - i) 

sortie pour 1-16:

>>> for n in range(1,17): 
    print(n, ': grid coords =', coordinates(n), 
      'Cartesian =',cartesianFromGrid(*coordinates(n))) 


1 : grid coords = (1, -1) Cartesian = (0, 0) 
2 : grid coords = (-1, -1) Cartesian = (-1, 0) 
3 : grid coords = (-1, 1) Cartesian = (-1, 1) 
4 : grid coords = (1, 1) Cartesian = (0, 1) 
5 : grid coords = (2, -1) Cartesian = (1, 0) 
6 : grid coords = (2, -2) Cartesian = (1, -1) 
7 : grid coords = (1, -2) Cartesian = (0, -1) 
8 : grid coords = (-1, -2) Cartesian = (-1, -1) 
9 : grid coords = (-2, -2) Cartesian = (-2, -1) 
10 : grid coords = (-2, -1) Cartesian = (-2, 0) 
11 : grid coords = (-2, 1) Cartesian = (-2, 1) 
12 : grid coords = (-2, 2) Cartesian = (-2, 2) 
13 : grid coords = (-1, 2) Cartesian = (-1, 2) 
14 : grid coords = (1, 2) Cartesian = (0, 2) 
15 : grid coords = (2, 2) Cartesian = (1, 2) 
16 : grid coords = (2, 1) Cartesian = (1, 1) 

Dans le cas où vous vous demandiez:

>>> coordinates(1000000) 
(500, 1) 

Cette dernière réponse est logique puisque l'un carré millionième est la clé de voûte d'une grille de 1000x1000 de carrés.

j'ai utilisé ci-dessus pour placer des carrés de couleur sur un widget toile tkinter:

enter image description here

+0

Toutes les réponses étaient bien pensées et constructives, mais la vôtre était la plus élégante et la plus résolue, avec le moins de code possible, un excellent travail! – Speedy

0

En supposant que vous utilisez l'indexation basée sur 1, vous pouvez utiliser l'algorithme suivant pour trouver votre réponse (pas sûr si une solution de forme fermée existe). L'idée sous-jacente est de trouver dans quelle couche vous vous trouvez (dans quelle mesure votre x ou votre y est éloigné de 0). Ensuite, trouvez le quadrant dans lequel vous vous trouvez. Ensuite, déterminez de quel côté vous êtes, puis déterminez les coordonnées.

get_coords(n) 
    layer = 1 
    while (n > 3 * layer * layer + (layer+1)*(layer+1)) 
     ++layer 
    layer_ind = n - 3 * (layer - 1) * (layer - 1) - layer * layer 
    if (layer_ind < 2 * layer + 1) # in bottom right 
     if (layer_ind <= layer + 1) 
      return (layer, - layer_ind + 1) 
     else 
      return (2 * layer + 1 - layer_ind, -layer) 
    else if ((layer_ind - 2 * layer - 1)/(2 * layer - 1) < 1) # bottom left 
     layer_ind = layer_ind - 2 * layer - 1 
     if (layer_ind <= layer) 
      return (-layer_ind+1, -layer+1) 
     else 
      return (-layer+1, -2*layer + 1 + layer_ind) 
    else if ((layer_ind - 2 * layer - 1)/(2 * layer - 1) < 2) # top left 
     layer_ind = layer_ind - 4 * layer 
     if (layer_ind <= layer) 
      return (-layer+1, layer_ind) 
     else 
      return (layer_ind - 2 * layer, layer) 
    else # top right 
     layer_ind = layer_ind - 6 * layer + 1 
     if (layer_ind <= layer) 
      return (layer_ind-1, layer) 
     else 
      return (layer-1, 2 * layer - layer_ind) 
1

Ma réponse précédente (maintenant reléguée à l'historique des révisions) était une solution récursive. Voici ce que j'appellerais une solution "cyclique". L'idée est que nous faisons plusieurs révolutions autour d'un carré. A tout indice donné:

  • Nous faisons une révolution autour de l'extérieur d'un petit carré
  • Nous sommes sur un côté de cette place
  • Nous sommes en partie sur un côté de cette place

En gardant une trace du carré, du côté et de la distance sur le côté, nous pouvons indexer chaque point.

On contourne un carré en dessinant 4 lignes égales. (Appelez-les "segments".) Nous commençons chaque révolution au milieu du premier segment.

Par exemple:

révolution 1 est de l'ordre d'un carré de taille 0, les segments de longueur 1:

  O OO 
O OO OO OO 

révolution 2 est d'environ un carré de taille 4, les segments de longueur 3:

  O  OOOO OOOO 
OO OO OOO OOO OOOO 
OOO OOO OOOO OOOO OOOO 
    O OOOO OOOO OOOO OOOO 

Revolution 3 est autour d'un carré de taille 16, les segments de longueur 5:

    O  OOOOOO OOOOOO 
OOOO OOOO OOOOO OOOOO OOOOOO 
OOOO OOOO OOOOO OOOOO OOOOOO 
OOOOO OOOOO OOOOOO OOOOOO OOOOOO 
OOOOO OOOOO OOOOOO OOOOOO OOOOOO 
    O OOOOOO OOOOOO OOOOOO OOOOOO 

Voici une implémentation en Python.

import math 

def makeSquare(main_index, offset_size): 
    step_index = int(math.sqrt(main_index))/2 # How many revolutions have we made? 
    last_square = int(math.pow(step_index*2,2)) # Whats the largest square less than main_index? 
    segment_length = (2 * step_index) + 1  # How long is the side of the square we've made so far? The segment length is 1 more than that. 

    main_index = main_index + step_index + 1 # Skip ahead so we start in the middle of the right side instead of the top. We do some modulo stuff below to wrap around. 

    segment_index = (main_index - last_square)/segment_length # Which segment are we on? 
    dot_index = main_index - segment_index*segment_length - last_square # How far along the segment are we? 

    draw_functions = [ 
     lambda i, size: [size,  size - i], # Draw the right side of a square 
     lambda i, size: [size - i, 0  ], # Draw the bottom of a square 
     lambda i, size: [0,  i  ], # Draw the left side of a square 
     lambda i, size: [i,  size ], # Draw the top of a square 
    ]  

    x, y = draw_functions[segment_index % 4](dot_index % (4 * segment_length), segment_length) 
    return [ x + offset_size - step_index - 1, y + offset_size - step_index - 1] 

# Print the points to the console in sequence 
import time, os 
step = 4 
points = [makeSquare(i, step) for i in range(int(math.pow(step*2,2)))] 
board = [[" " for x in range(step*2)] for x in range(step*2)] 
for p in range(len(points)): 
    print 
    print p, "------------------" 
    board[step*2-1 - points[p][1]][points[p][0]] = "O" # Do some coordinate wrangling so we have normal X and Y 
    print (os.linesep).join([''.join(row) for row in board]) 
    time.sleep(.1)