Stroop效应的改组算法

时间:2017-12-30 23:38:17

标签: c# .net algorithm

我正在开发一个小型C# Winforms应用程序,该应用程序复制了几个关于认知心理学的视觉实验。我没有使用预制图像,而是试图以编程方式创建它们以培养我的技能。

现在我正在处理Stroop Effect,我试图重现以下图片:

Example

我准备了两个不同的数组,一个包含颜色,另一个包含颜色名称:

Color[] colors =
{
    Color.Blue,
    Color.Red,
    Color.Black,
    Color.Green
};

String[] names =
{
    "BLUE",
    "RED",
    "BLACK",
    "GREEN"
};

第一步很简单。我所要做的就是使用各自的ForegroundColor绘制字符串,这样names[0]将使用colors[0]打印,names[1]将使用colors[1]打印等等...

这是艰巨的任务。一旦字符串数组被洗牌:

Random r = new Random();
String[] namesRandom = names.OrderBy(x => r.Next()).ToArray();   

我也必须改组colors数组。但必须确保没有颜色与其正确的名称相匹配。因此,例如,这将是正确

"BLACK" Color.Green
"GREEN" Color.Red
"RED"   Color.Blue
"BLUE"  Color.Black

这将是错误

"BLACK" Color.Black -> Matching Color/Name
"GREEN" Color.Red
"RED"   Color.Blue
"BLUE"  Color.Green

有关如何有效完成此任务的任何想法?我想避免使用shuffle until condition is met方法。

P.S。 =在Bitmap上绘制字符串不是问题,我在这项开发任务上不需要帮助。

[编辑]

对于因为无法将shuffle until condition is met的概念与几行代码相关联的10k-ers或者因为他们认为我在玩俄罗斯方块时试图完成我的工作所致的10k-ers是我当前算法的快速实现:

public class Program
{
    private static Random s_Random = new Random();

    public static void Main(String[] args)
    {
        Color[] colors =
        {
            Color.Blue,
            Color.Red,
            Color.Black,
            Color.Green
        };

        String[] names =
        {
            "BLUE",
            "RED",
            "BLACK",
            "GREEN"
        };

        while (MatchingPairs(colors, names)) 
        {
            colors = Shuffle(colors);
            names = Shuffle(names);
        }

        for (Int32 i = 0; i < colors.Length; ++i)
            Console.WriteLine(colors[i].Name.ToUpperInvariant() + " | " + names[i]);
    }

    private static Boolean MatchingPairs(Color[] colors, String[] names)
    {
        for (Int32 i = 0; i < colors.Length; ++i)
        {
            if (colors[i].Name.ToUpperInvariant() == names[i])
                return true;
        }

        return false;
    }

    private static T[] Shuffle<T>(T[] array)
    {
        return array.OrderBy(x => s_Random.Next()).ToArray();
    }
}

所以,正如你所看到的,我可以用手指键入代码。我只是想知道是否有更好的方法来获得相同的结果。

4 个答案:

答案 0 :(得分:1)

您可以执行以下操作:

  1. 为每个名称实施有效颜色规则:

    Func<string, IEnumerable<Color>> validColorsRule = s =>
    {
        switch (s)
        {
            case "BLUE": return colors.Except(new[] { Color.Blue });
            case "RED": return colors.Except(new[] { Color.Red });
            case "BLACK": return colors.Except(new[] { Color.Black });
            case "GREEN": return colors.Except(new[] { Color.Green });
            default: throw new NotSupportedException();
        }
    };
    
  2. 使用Color

    构建已混乱的Enumerable.Aggregate数组
    Color[] colorRandom =
        namesRandom.Aggregate(Enumerable.Empty<Color>(),
                              (acc, n) => 
                                  acc.Concat(new[] { 
                                     validColorsRule(n).Except(acc)
                                                       .OrderBy(x => r.Next())
                                                       .FirstOrDefault() }))
                   .ToArray();
    

答案 1 :(得分:1)

您可以将其重新表述为伪随机问题。

  
      
  1. 生成一个介于1和n-1之间的随机整数(比如i)并移动颜色数组(使用它的副本)i
  2.   
  3. 现在生成0到n-1之间的随机序列,无需替换,并打印名称和颜色
  4.   
static void Main(string[] args)
{
            ConsoleColor[] colors =
       {
            ConsoleColor.Blue,
            ConsoleColor.Red,
            ConsoleColor.Black,
            ConsoleColor.Green
        };

            String[] names =
            {
            "BLUE",
            "RED",
            "BLACK",
            "GREEN"
        };

            Random r = new Random();
            int i = 0;
            while(i==0)
                i=r.Next() % (names.Length-1);
            List<int> rndList = Enumerable.Range(0,names.Length).OrderBy(x => r.Next()).ToList();
            Console.BackgroundColor = ConsoleColor.White;
            foreach(int j in rndList)
            {
                int k = (j+i) % (colors.Length);
                Console.ForegroundColor = colors[k];
                Console.Write(names[j] + " ");
            }
}

答案 2 :(得分:1)

我认为你正试图创造一个随机的紊乱。对此进行网络搜索可以找到一篇关于在http://epubs.siam.org/doi/pdf/10.1137/1.9781611972986.7 - &#34;生成随机紊乱&#34;高效生成此类内容的论文。由Martinez,Panholzer和Prodinger完成。然而,本文还表明,即使n可能非常大,随机选择的排列是一个紊乱的机会仍然非常接近1 / e,所以简单的解决方案就是生成一个随机排列并检查它是否是一种紊乱,重复直到这是真的,在实践中可能足以达到大多数目的。

答案 3 :(得分:1)

这可能不是最好的方法,但它应该可以正常工作。解决此问题的一种方法是分配随机名称,直到只剩下两个(where index i == Count - 2)。此时,如果索引i + 1处的项目名称是其余选项之一,请将其用于索引为i的项目。

以下是一些使用控制台应用程序(和ConsoleColor)的示例代码,但概念是相同的。我使用了一个结合ConsoleColorstring的类,所以如果你想使用数组,则需要进行一些修改。

首先是课程:

class NamedColor
{
    public ConsoleColor Color { get; set; }
    public string Name { get; set; }
    public NamedColor(ConsoleColor color, string name)
    {
        Color = color;
        Name = name;
    }
}

一种将这些对象的列表打印到控制台的方法:

static void PrintColors(IEnumerable<NamedColor> colors)
{
    Console.BackgroundColor = ConsoleColor.Gray;
    Console.Write(new string(' ', 80));

    foreach (var color in colors)
    {
        Console.ForegroundColor = color.Color;
        Console.Write(color.Name + " ");
    }

    Console.Write(new string(' ', 139)); // Ugly, hard-coded length for simplicity
}

然后我们可以创建一个方法来获取这些对象的列表,对它们之间的所有名称进行洗牌,然后返回该新列表:

static NamedColor[] ShuffleNames(NamedColor[] colors)
{
    var names = colors.Select(c => c.Name).ToList();
    var shuffled = new NamedColor[colors.Length];

    for (int i = 0; i < colors.Length; i++)
    {
        string name;

        // If there are only two items left, and our list of names contains 
        // the *next* item's name, then we must take that name for this item
        if (i == colors.Length - 2 && names.Contains(colors[i + 1].Name))
        {
            name = colors[i + 1].Name;
        }
        else
        {
            // Choose a random name from all names except this item's name
            var candidateNames = names.Where(n => !n.Equals(colors[i].Name)).ToList();
            name = candidateNames[rnd.Next(candidateNames.Count)];
        }

        shuffled[i] = new NamedColor(colors[i].Color, name);
        names.Remove(name);
    }

    return shuffled;
}

然后我们可以随机播放列表,然后将名称洗牌,然后打印出如下列表:

private static Random rnd = new Random();

static void Main(string[] args)
{
    var correctColors = new NamedColor[]
    {
        new NamedColor(ConsoleColor.Blue, "BLUE"),
        new NamedColor(ConsoleColor.Black, "BLACK"),
        new NamedColor(ConsoleColor.Red, "RED"),
        new NamedColor(ConsoleColor.Green, "GREEN"),
    };

    PrintColors(correctColors);

    for (int count = 0; count < 10; count++)
    {
        // Shuffle the items, then shuffle the names
        var shuffledColors = correctColors.OrderBy(c => rnd.NextDouble()).ToArray();
        shuffledColors = ShuffleNames(shuffledColors);

        PrintColors(shuffledColors);
    }

    Console.ResetColor();
    Console.WriteLine("\nDone!\nPress any key to exit...");
    Console.ReadKey();
}

<强>输出

enter image description here