农民需要通过自引用动物表循环的算法

时间:2013-01-04 04:57:48

标签: c# algorithm design-patterns database-design

问题:农场里有大量的动物。每个动物都可以拥有任何数量的动物朋友,除了反社会动物 - 他们没有属于他们的朋友,但他们作为朋友属于其他正常动物。除了反社会动物之外,每只动物都是最不开心的动物朋友。反社会动物幸福的水平可以是任何东西。

一天早上,所有的动物都醒来,发现一些反社会动物的情绪发生了变化。农夫如何找出每只动物的幸福?

就牧场的手而言(他们没有去农场学校):

one-to-many self-referencing table

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)的情绪。每天都有新动物被甩掉。我认为解决方案可能是一种常见的算法设计,但我无法找到它。我不相信我有正确的术语来准确搜索它。

非常感谢,如果这已经得到了答复,我很抱歉!

加了:

这是可能关系的贫民窟油漆图:

like an acyclic graph

反社会动物处于底层,没有属于他们的朋友。正常的动物在上面的其他地方。正常的动物友谊没有确切的结构,除了(正如塞巴斯蒂安指出的那样)不能有一个闭环(如果设计正确)。

每周会有成千上万的动物被添加,处理速度是一个关键因素。

6 个答案:

答案 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);
   }