问题:农场里有大量的动物。每个动物都可以拥有任何数量的动物朋友,除了反社会动物 - 他们没有属于他们的朋友,但他们作为朋友属于其他正常动物。除了反社会动物之外,每只动物都是最不开心的动物朋友。反社会动物幸福的水平可以是任何东西。
一天早上,所有的动物都醒来,发现一些反社会动物的情绪发生了变化。农夫如何找出每只动物的幸福?
就牧场的手而言(他们没有去农场学校):
DataTable animals = Select_All_Animals();
foreach (DataRow animal in animals.Rows)
{
int worstMood = 10; //Super Happy!
DataTable friendRecords = Select_Comp_Animal_AnimalFriend((int)animal["AnimalID"]);
foreach (DataRow friend in friendRecords.Rows)
{
DataTable animalFriends = Select_AnimalFriend((int)friend["AnimalID_Friend"]);
foreach (DataRow animalFriend in animalFriends.Rows)
{
int animalMood = Get_Animal_Mood((int)animalFriend["Mood"]);
if (animalMood < worstMood)
{
worstMood = animalMood;
}
}
}
}
但这不起作用,因为动物表不会按顺序跟随已形成的动物朋友等级。动物可以随时互相交朋友!所以Animal(1)可能有动物(4000)作为朋友。动物(1)不会表现出准确的情绪,因为它会在动物(4000)的情绪自我更新之前检查动物(4000)的情绪。每天都有新动物被甩掉。我认为解决方案可能是一种常见的算法设计,但我无法找到它。我不相信我有正确的术语来准确搜索它。
非常感谢,如果这已经得到了答复,我很抱歉!
加了:
这是可能关系的贫民窟油漆图:
反社会动物处于底层,没有属于他们的朋友。正常的动物在上面的其他地方。正常的动物友谊没有确切的结构,除了(正如塞巴斯蒂安指出的那样)不能有一个闭环(如果设计正确)。
每周会有成千上万的动物被添加,处理速度是一个关键因素。
答案 0 :(得分:5)
首先抓住所有反社会的动物并命令他们从最开心到最开心。最大限度地将所有社交动物的快乐初始化(这使得一切变得更容易,因为您不必检测以前不快乐的动物何时变得更快乐)。然后只需遍历列表并将幸福级别传播到友谊链中:
void UpdateFarm()
{
// Start with a list of antisocial animals from least to most happy.
var antisocialAnimals = GetAntisocialAnimals().OrderBy(x => x.Happiness);
// Initialise the social animals to the global maximum. Note the
// global maximum is the happiest antisocial animal. This is done to
// avoid the case where an antisocial animal's happiness has increased,
// so some of the social animals are too unhappy.
var maxHappiness = antisocialAnimals.Last().Happiness;
var socialAnimals = GetSocialAnimals();
foreach (var socialAnimal in socialAnimals)
socialAnimal.Happiness = maxHappiness;
// Now iterate from least to most happy, propagating up the friend chain.
foreach (var antisocialAnimal in antisocialAnimals)
UpdateFriends(antisocialAnimal);
}
// To propagate a happiness change, we just find all friends with a higher
// happiness and then lower them, then find their friends and so on.
void UpdateFriends(Animal animal)
{
var friends = GetFriends(animal); // Friends with this animal, not friends of.
foreach (var friend in friends.Where(x => x.Happiness > animal.Happiness))
{
friend.Happiness = animal.Happiness;
// Since this friend's happiness has changed, we now need to update
// its friends too.
UpdateFriends(friend);
}
}
答案 1 :(得分:4)
好问题。所以,如果我理解正确你基本上有循环的有向图。您可以将每只动物视为一个节点(顶点),它具有动物所依赖的动物边缘,以及来自动物的传入边缘,它会影响它。显然,反社会动物只会有传入的边缘。
如果你这么想,你会注意到两件事
1)您可以设计一个迭代算法,扫描所有动物,检查每只动物是否有任何外向边缘只有社交或已经处理的动物,如果是,我们计算动物的情绪,并标记它经过处理。如果没有,我们只是跳过它进行迭代。
2)因为您的图表中可以有循环,所以此算法并不总是完成。那就是你可能有动物A取决于B,B取决于C和C取决于A.如果你有简单的逻辑,如你的情况,最低的情绪获胜,你可以通过分配给所有人来检测和解决这些周期动物在循环中 - 在这种情况下A,B,C最低的共同情绪。
希望它有所帮助!
答案 2 :(得分:1)
有趣的问题。如果我理解正确的话,如果它是单向的朋友(入境),每只动物的幸福就是所有人幸福的最小点。
如果是这样的话,那么挑战在于每个动物朋友的幸福都是由他们的朋友决定的,所以从哪里开始。
这是我第一眼看到的......
由于新动物每天出现,你需要每天开始新鲜,你需要从最低起始幸福的动物开始。很容易找到那些,然后将这种幸福传播给他们所有的动物,相应地调整这些幸福水平。然后取出所有调整过的动物并重复直至不再调整动物。一旦幸福水平得到充分传播,就要进入下一个最高幸福水平并重复,直到处理剩余的最高幸福水平。
有一点有趣的是,哪些动物(如果有的话)具有反社会性并不重要。它们只是没有影响力的动物,因此不会通过这个过程进行调整。
我认为这会让你达到你想要的目标,而且编码应该不难。
希望它有所帮助。
答案 3 :(得分:1)
因此,这表明了以下算法:
开始时你将社交动物情绪设置为MAX_MOOD,然后按照心情增加的顺序循环反社会动物,并递归地更新所有朋友和朋友的心情。如果在递归期间你没有更新情绪,那么你可以停止递归,这就是为什么你会按照心情增加的顺序循环:尽可能“阻止”未来的递归。
这类似于Sebastian K的“标记为已处理”,尽管您不需要单独列出已处理的内容,因为情绪本身包含此信息。这也将实现他的解决循环问题,因为递归将遍历循环一次并停止。
答案 4 :(得分:1)
还有其他更合适的答案,但是我会把它作为一个有趣的思想实验扔出去 - 它在数据库中真的很毛茸茸,而且效率低下:
任何动物的幸福程度都是当前幸福的关系,被其所喜欢的动物“拉”或“推”。
因此,一只动物的幸福水平的变化会引起一波调整,可能会影响所有动物。
将“友谊图”描绘成一堆通过弹簧连接到其他盒子的盒子。这些盒子显然代表了动物,弹簧对邻近动物产生了“影响”。
拉出一个盒子,一圈动作从移动的盒子中伸展出来;这反过来最终会导致反向涟漪效应 - 我让你更快乐,所以反过来你最终会随着时间的推移让我更幸福(或更悲伤)从这种关系的影响。
“但它会不断调整自己而不会陷入稳定的状态!”输入一个“摩擦”术语,抵消微小的运动(比如在某个阈值下,“拉”不能克服悲伤/幸福的惯性)。
听起来很像Craig的博客行为,他的名字是什么。 (需要参考) - 再次,我必须强调这对于你想要做的事情来说绝对不是最优的,但我总是喜欢从多个角度看问题。
答案 5 :(得分:1)
听起来像家庭作业,但我会有一个裂缝,我从其他人那里得到了一两个想法:
public void MoodCalculator()
{
/** Reasoning:
*
* The unidirectional cyclic graph can be also expressed as multiple friend
* trees where
* anti-social animal is the root node of a friend tree. Because a social animal's
* mood is that of its lowest mood friend this means that regardless of the
* tree depth of an animal
* it's mood is that of the mood of the lowest mood antisocial animal
* who's tree it is a member of.
*
* */
SortedList<int, List<Animal>> moodGroups = new SortedList<int, List<Animal>>();
HashSet<Animal> allAnimals = new HashSet<Animal>();
//get all antisocial animals and group according to mood
//work from low to high mood
forach(Animal a in GetAllAntiSocialAnimalsFromDb())
{
if(!moodGroups.ContainsKey(a.Mood)) moodGroups.Add(a.Mood, new List<Animal>());
moodGroups[a.Mood].Add(a);
}
foreach(var item in moodGroups)
{
//add our root antisocial animals to master group
foreach(Animal a in item.Value) allAnimals.Add(a);
//recurse over tree starting at antisocial animal roots
TreeRecurse(item.Value, allAnimals, item.Key);
}
}
public void TreeRecurse(List<Animal> children, List<Animal> allAnimals, int mood)
{
//can look for list as we are just grouping everything and don't care about tree
//structure
//db call should only get unique/distinct animals
List<Animal> parentAnimals = GetParentAnimalsOfChildAnimalListFromDb(children);
//remove animals from from parents if they are already in the allAnimal set
//working from low mood to high we can ignore animals that have appeared in a
//lower mood tree
parentAnimals.RemoveAll(a => allAnimals.Contains(a));
//add animals to allAnimals and set mood
foreach (Animal a in parentAnimals)
{
a.Mood = mood;
allAnimals.Add(a);
}
//end recurse
if (parentAnimals.Count == 0) return;
//recurse
else TreeRecurse(parentAnimals, allAnimals, mood);
}