Использование кортежей Python в качестве векторов - PullRequest
4 голосов
/ 05 апреля 2010

Мне нужно представить неизменяемые векторы в Python («векторы» как в линейной алгебре, а не в программировании). Кортеж кажется очевидным выбором.

Проблема в том, что мне нужно реализовать такие вещи, как сложение и скалярное умножение. Если a и b - векторы, а c - число, лучшее, что я могу придумать, это:

tuple(map(lambda x,y: x + y, a, b)) # add vectors 'a' and 'b'
tuple(map(lambda x: x * c, a))      # multiply vector 'a' by scalar 'c'

, который кажется не элегантным; должен быть более ясный и простой способ сделать это - не говоря уже о том, чтобы избежать вызова tuple, поскольку map возвращает список.

Есть ли лучший вариант?

Ответы [ 7 ]

10 голосов
/ 05 апреля 2010

Неизменяемые типы довольно редки в Python и сторонних расширениях; ОП справедливо заявляет, что «линейной алгебры достаточно для использования, поэтому мне кажется, что я не должен бросать свою собственную», - но все известные мне типы, которые используют линейную алгебру, изменчивы ! Таким образом, поскольку ОП непреклонен в неизменности, то - это ничего для него, кроме маршрута «покатай свой собственный».

Не то, чтобы в этом участвовало столько всего, например, если вам конкретно нужны двумерные векторы:

import math
class ImmutableVector(object):
    __slots__ = ('_d',)
    def __init__(self, x, y):
        object.__setattr__(self, _d, (x, y))
    def __setattr__(self, n, v):
        raise ValueError("Can't alter instance of %s" % type(self))
    @property
    def x(self): 
        return self._d[0]
    @property
    def y(self):
        return self._d[1]
    def __eq__(self, other):
        return self._d == other._d
    def __ne__(self, other):
        return self._d != other._d
    def __hash__(self):
        return hash(self._d)
    def __add__(self, other):
        return type(self)(self.x+other.x, self.y+other.y)
    def __mul__(self, scalar):
        return type(self)(self.x*scalar, self.y*scalar)
    def __repr__(self):
        return '%s(%s, %s)' % (type(self).__name__, self.x, self.y)
    def __abs__(self):
        return math.hypot(self.x, self.y)

Я «добавил бесплатно» несколько дополнительных функций, таких как .x и .y Свойства R / O, хорошее представление строк, удобство использования в наборах или в качестве ключей в словах (почему еще нужно иметь неизменность? -), малый объем памяти, abs(v) для длины вектора v - я уверен, что вы можете подумать о других методах и операторах "не будет круто, если", в зависимости от области применения и они будут такими же легкими. Если вам нужны другие измерения, это будет не намного сложнее, хотя немного менее читабельно, поскольку нотация .x, .y больше не применяется ;-) (но я бы использовал genexps, а не map) .

9 голосов
/ 05 апреля 2010

NumPy поддерживает различные алгебраические операции со своими массивами.

8 голосов
/ 05 апреля 2010

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

class Vector(tuple):
    def __add__(self, a):
        # TODO: check lengths are compatable.
        return Vector(x + y for x, y in zip(self, a))
    def __mul__(self, c):
        return Vector(x * c for x in self)
    def __rmul__(self, c):
        return Vector(c * x for x in self)

a = Vector((1, 2, 3))
b = Vector((2, 3, 4))

print a + b
print 3 * a
print a * 3
5 голосов
/ 18 июля 2012

Хотя использование библиотеки, такой как NumPy, похоже, является решением для OP, я думаю, что есть простое решение, которое не требует дополнительных библиотек и которое вы можете оставить неизменным, с итерациями.

Использование модулей itertools и operators:

imap(add, a, b) # returns iterable to sum of a and b vectors

Эта реализация проста. Он не использует лямбда-выражения и преобразования списков-кортежей, поскольку он основан на итераторах.

from itertools import imap
from operator import add
vec1 = (1, 2, 3)
vec2 = (10, 20, 30)
result = imap(add, vec1, vec2)
print(tuple(result))

Урожайность:

(11, 22, 33)
3 голосов
/ 05 апреля 2010

Почему бы не создать свой собственный класс, используя 2 декартовых точечных переменных-члена? (извините, если синтаксис немного нарушен, мой питон ржавый)

class point:
    def __init__(self,x,y):
        self.x=x
        self.y=y
        #etc

    def add(self,p):
        return point(self.x + p.x, self.y + p.y)

class vector:
    def __init__(self,a,b):
        self.pointA=a
        self.pointB=b
        #etc

    def add(self,v):
        return vector(self.pointA + v.pointA, self.pointB + v.pointB)
0 голосов
/ 25 июня 2016

Для периодического использования возможно решение Python 3 без повторяющихся лямбд с помощью стандартного пакета оператора:

from operator import add, mul

a = (1, 2, 3)
b = (4, 5, 6)

print(tuple(map(add, a , b)))
print(tuple(map(mul, a , b)))

который печатает:

(5, 7, 9)
(4, 10, 18)

Для серьезных линейных алгебраических вычислений с использованием числовых векторов это каноническое решение:

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(a+b)
print(a*b)

который печатает:

[5 7 9]
[ 4 10 18]
0 голосов
/ 05 апреля 2010

Поскольку почти все функции манипулирования последовательностями возвращают списки, это почти то, что вам нужно сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...