英文:
avoiding duplicate instances of an object with random variables
问题
我一直在努力寻找一个足够简单的解决方案来解决以下问题:
假设我有一个类,其中定义了两个变量,如下所示(在x-y网格上的点):
```python
class Target:
def __init__(self):
self.x = random.randint(1, 9)
self.y = random.randint(1, 9)
我需要生成一定数量的这些对象,比如说10个:
for i in range(10):
t = Target()
当然,有时坐标会重叠。
如何确保没有任何对象具有相同的x和y变量,这将是最简单的方法?我尝试过一些解决方案,但它们感觉过于复杂,我觉得应该有一个相对简单的方法。
<details>
<summary>英文:</summary>
I've been struggling to find a simple enough solution to the following problem:
Let's say I have a class with two variables defined, like below (points on a x-y grid):
```python
class Target:
def __init__(self):
self.x = random.randint(1, 9)
self.y = random.randint(1, 9)
I need to generate a given amount of those, let's say 10:
for i in range(10):
t = Target()
Of course, sometimes the coordinates overlap.
What would be the simplest way to assure none of the objects have the same x and y variables? I've been trying some solutions, but they feel overwrought and I feel there must be a relatively simple one.
答案1
得分: 2
首先,调整你的类以将 x
和 y
坐标作为初始化参数。
class Target:
def __init__(self, x, y):
self.x = x
self.y = y
然后,你可以生成一个包含所有有效坐标对的列表。
xy = [(x, y) for x in range(1, 10) for y in range(1, 10)]
然后,无重复地随机抽取大小为 10 的样本。
xy_sample = random.sample(xy, 10)
然后初始化 10 个类的实例。
targets = [Target(x, y) for x, y in xy_sample]
注意:如果坐标空间非常庞大,那么仅在集合中跟踪生成的 x
和 y
对,以及在极少情况下命中重复时重新生成随机数可能更加可行。
英文:
First, adjust your class to take the x
and y
coordinates as initialization parameters.
class Target:
def __init__(self, x, y):
self.x = x
self.y = y
Then you could generate a list of all valid coordinate pairs.
xy = [(x, y) for x in range(1, 10) for y in range(1, 10)]
Then take a random sample of size 10 without replacement.
xy_sample = random.sample(xy, 10)
Then initialize 10 instances of your class.
targets = [Target(x, y) for x, y in xy_sample]
Caveat: if the xy-space is huge then just keeping track of the generated x
, y
pairs in a set and rerolling the randomly generated numbers in the unlikely case that you hit a duplicate may be more feasible.
答案2
得分: 2
假设您的类构造函数的接口已设置,而且您无法更改它(或者不想更改它),所以随机化部分需要放在__init__
方法内。并且假设您主要希望"确保"在Target
对象列表中没有重复项,而不对您确切要实现的目标做任何进一步的假设。
首先,您需要使Target
对象之间可比较,基于它们的x
和y
值。这可以在__eq__
方法中实现:
def __eq__(self, other):
return self.x == other.x and self.y == other.y
此外,您需要创建__hash__
方法,这是要求对于基于__eq__
相等的对象必须相等的,因为重新实现__eq__
会重置默认的__hash__
,所以Target
对象没有哈希值,您可以这样实现:
def __hash__(self):
return hash((self.x, self.y)) # 只需使用元组的哈希值
现在,您可以基于它们的(x,y)
数据来比较Target
对象:
Target() == Target()
这在以前也可以工作,但会返回这两个目标对象是否相同,即使它们具有相同的x和y值也可能返回False
。
基于这个基础,您现在可以断言您的目标对象列表中没有重复项:
targets = [Target() for in range(10)]
通过将其转换为一个set
,这将基于__eq__
的相等性来删除重复项,然后比较结果的长度:
assert len(targets) == len(set(targets))
这可能不是做的最有意义的事情,但它是对您问题的直接答案。
英文:
Assuming your class constructor's interface is set and you can't change it (or don't want to), so the randomization part needs to be inside __init__
. And also assuming that just "assuring" there are no dupes in a list of Target
s is primarily what you want, without any further assumptions made on what you're exactly trying to achieve.
Then, as a first step, you would require for the Target
s to be comparable to each other, based on their x
and y
values.This can be implemented in the __eq__
method:
def __eq__(self, other):
return self.x == other.x and self.y == other.y
Alongside, you need to create __hash__
, which is required to be equal for objects that are equal based on __eq__
, and also because reimplementing __eq__
resets the default __hash__
so Target
wouldn't be hashable without:
def __hash__(self):
return hash((self.x, self.y)) # just use the tuple's hash
Now you can compare Target
s with one another based on their (x,y)
data:
Target() == Target()
This worked before, too, but would return if the two targets are the same object, which may be False
even with the same x and y.
With this as the basis, you can now assert there are no dupes in your list of targets
targets = [Target() for in range(10)]
by turning it into a set
, which removes dupes based on equality based on __eq__
, and then comparing the length of the result:
assert len(targets) == len(set(targets))
This is may not be what makes the most sense to do, but a direct answer to your question nonetheless.
答案3
得分: 1
这使用一个记住旧选择的生成器。
小心:如果创建的Target类的数量超过可用组合,该函数可能会变成无限循环...
import numpy as np
class CustomGenerator(object):
def __init__(self, lower_bound: int, upper_bound: int):
self.old_values = []
self.lower_bound = lower_bound
self.upper_bound = upper_bound
def __iter__(self):
return self
def __next__(self):
return self.next()
def next(self):
while True:
new_vals = tuple(np.random.randint(self.lower_bound, self.upper_bound, 2))
if new_vals not in self.old_values:
break
return new_vals
gen = CustomGenerator(0, 100)
class Target:
def __init__(self, gen):
self.x, self.y = next(gen)
def __repr__(self) -> str:
return f"{self.x}, {self.y}"
t = Target(gen)
print(t)
英文:
This uses a generator that remebers the old choices.
Carefull: The function can turn into an infinity loop if you create more classes of Target than there are available combinations...
import numpy as np
class CustomGenerator(object):
def __init__(self, lower_bound: int, upper_bound: int):
self.old_values = []
self.lower_bound = lower_bound
self.upper_bound = upper_bound
def __iter__(self):
return self
def __next__(self):
return self.next()
def next(self):
while True:
new_vals = tuple(np.random.randint(self.lower_bound, self.upper_bound, 2))
if new_vals not in self.old_values:
break
return new_vals
gen = CustomGenerator(0, 100)
class Target:
def __init__(self, gen):
self.x, self.y = next(gen)
def __repr__(self) -> str:
return f"{self.x}, {self.y}"
t = Target(gen)
print(t)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论