【解惑】孜孜不倦,用足球赛程详解c#中的yield return用法

这篇具有很好参考价值的文章主要介绍了【解惑】孜孜不倦,用足球赛程详解c#中的yield return用法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在一个知名企业赞助的足球联赛中,有256支球队参赛。为了确保比赛的顺利进行,企业指派了小悦负责熬夜加班制定每一个球队的赛程。尽管她对足球的了解并不多,但是她对待工作的认真态度却让人钦佩。

在小悦的努力下,她顺利完成了第一轮、第二轮和第三轮的比赛安排。然而,在大赛开始前的模拟比赛中,她发现了一个严重的问题:由于参赛球队过多,人为的安排总会导致一些参赛球队被遗漏了比赛。这让她十分焦虑,因为如果不能尽快解决这个问题,联赛的公平性和竞争性将受到严重影响。

为了解决这个问题,小悦开始了她的电话咨询之旅。她先是联系了赛事主办方,了解参赛球队的具体情况。随后,她又联系了计算机专家,希望找到一个解决办法,确保每个参赛球队都能顺利比赛。

在与计算机专家沟通的过程中,小悦了解到这个问题并不简单,因为要考虑到的因素非常多。不过,在专家的帮助下,她逐渐找到了问题的根源,并听取专家的意见采取了一些有效的措施来解决它。最终,经过小悦的不懈努力和计算机专家的协助,每个参赛球队的赛程都被安排得合理而公正。

小悦在熬夜加班制定赛程时,扎着马尾辫,聚精会神地盯着电脑屏幕,手指在键盘上飞快地敲击着。她的眼神中闪烁着智慧的光芒,仿佛在告诉人们:她有能力解决任何问题。当她下意识挽起头发时,这个简单的动作,展现了她的优雅和美丽。

在这个过程中,小悦学到了很多关于足球和赛程安排的知识。她也深刻体会到团队合作的重要性,学会了如何与同事合作解决问题。最终,她的努力得到了上司和公司的肯定,也为她带来了不少宝贵的经验。

小悦面临的问题如下:锦标赛由256支队伍组成。这是一场循环赛(全部比赛),所以有255轮,每支球队每轮比赛一次。每支球队在锦标赛中与其他球队对抗一次(每场比赛在锦标赛中不会重复)。

 她的任务是实现一个函数buildMatchesTable,它接收团队的数量(总是正数和偶数)并返回一个矩阵。

 矩阵中的每一行代表一轮。矩阵的每一列表示一个匹配。这场比赛是由两支队伍组成的一个阵列。每个团队都用一个数字表示,从1开始直到团队数量。

 示例(假设4只球队参赛): 构建匹配表(4)

应该返回一个元组矩阵,如下所示:

{

new[]{(1,2),(3,4)},//第一轮:1对2,3对4

new[]{(1,3),(2,4)},//第二轮:1对3,2对4

new[]{(1,4),(2,3)}//第三轮:1对4,2对3

}


算法实现:

 1 public static (int, int)[][] BuildMatchesTable(int n)
 2 {
 3     var matches = new (int, int)[n - 1][];  // 创建一个二维数组,表示比赛表
 4     var teams = FairCycle(n).GetEnumerator();  // 获取一个循环迭代器,用于生成比赛对阵
 5     for (int i = 0; i < n - 1; i++)
 6     {
 7         var round = new (int, int)[n / 2];  // 创建一个一维数组,表示当前轮次的比赛对阵
 8         for (int t = 0; t < n / 2; t++)
 9         {
10             teams.MoveNext();  // 迭代到下一个比赛对阵
11             round[t] = teams.Current;  // 将当前比赛对阵添加到当前轮次的比赛表中
12         }
13         matches[i] = round;  // 将当前轮次的比赛表添加到总的比赛表中
14         // FairCycle函数会在这里负责循环到下一个比赛对阵
15     }
16     return matches;  // 返回生成的比赛表
17 }
18 
19 static IEnumerable<(int,int)> FairCycle(int n)
20 {
21     // 将数字1到n-1按顺时针方式排列
22     // 从12点钟方向开始,1位于12点钟位置
23     int p = 1;
24     while (true)
25     {
26         // 返回n和12点钟位置的数字
27         yield return (p, n);
28         // 然后返回12点钟左右两侧的数字配对
29         // 例如,11和1点钟,10和2点钟,以此类推
30         for (int i = 1; i < n / 2; i++)
31         {
32             int l = p + i;
33             l = l > (n - 1) ? l - (n - 1) : l;  // 对n进行循环,但不包括n本身
34             int r = p + (n - 1) - i;
35             r = r > (n - 1) ? r - (n - 1) : r;
36             yield return (l, r);
37         }
38         // 将时钟旋转n/2,使得p + n/2现在位于12点钟位置
39         p += n / 2;
40         p = p > (n - 1) ? p - (n - 1) : p;
41         // 这将开始下一轮的配对
42     }
43 }

这段代码实现了一个生成比赛表的函数`BuildMatchesTable`和一个辅助函数`FairCycle`。

`BuildMatchesTable`函数接收一个正数n作为参数,返回一个二维数组,表示比赛表。

`FairCycle`函数是一个模拟时钟循环迭代器,用于生成球队比赛对阵表。

让我们以`BuildMatchesTable(4)`为例来详细介绍`yield return`的运行过程和直接使用`return`的运行过程:

首先,我们调用`BuildMatchesTable(4)`函数,它将生成一个4个参与者的比赛表。

执行`var teams = FairCycle(n).GetEnumerator();`时会调用`FairCycle(n)`方法,并开始执行其中的代码。在`FairCycle(n)`方法中,第一次调用`yield return`语句会返回一个`IEnumerator`对象,该对象用于迭代生成比赛对阵。

这意味着在执行`var teams = FairCycle(n).GetEnumerator();`之后,`teams`变量将持有一个可以用于迭代生成比赛对阵的迭代器对象。你可以使用`teams.MoveNext()`方法来逐个获取比赛对阵,每次调用`MoveNext()`方法时,都会执行`FairCycle(n)`方法中的代码,直到遇到下一个`yield return`语句。

使用`yield return`的运行过程如下:

1. 初始化时钟位置p为1。
2. 进入无限循环。
3. 第一次调用`yield return`语句,返回当前时钟位置p和数字n的配对`(p, n)`,即`(1, 4)`。此时,比赛表中的第一场比赛是1号选手对阵4号选手。
4. 继续循环,进入第二次调用`yield return`语句。
- 计算左侧数字l,它等于p加上i,即2。
- 计算右侧数字r,它等于p加上(n-1)-i,即3。
- 使用`yield return`语句返回左侧数字l和右侧数字r的配对`(l, r)`,即`(2, 3)`。此时,比赛表中的第二场比赛是2号选手对阵3号选手。
5. 更新时钟位置p,使其加上n/2,即2。此时,时钟位置p指向下一轮的起始位置。
6. 回到步骤3,开始下一轮的配对。
- 第三次调用`yield return`语句,返回当前时钟位置p和数字n的配对`(p, n)`,即`(2, 4)`。此时,比赛表中的第三场比赛是2号选手对阵4号选手。
- 继续循环,进入第四次调用`yield return`语句。
- 计算左侧数字l,它等于p加上i,即3。
- 计算右侧数字r,它等于p加上(n-1)-i,即2。
- 使用`yield return`语句返回左侧数字l和右侧数字r的配对`(l, r)`,即`(3, 2)`。此时,比赛表中的第四场比赛是3号选手对阵2号选手。
- 更新时钟位置p,使其加上n/2,即4。此时,时钟位置p指向下一轮的起始位置。
- 回到步骤3,开始下一轮的配对。由于此时时钟位置p超过了(n-1),即4,所以将其减去(n-1),即得到1。此时,时钟位置p重新指向下一轮的起始位置。
- 第五次调用`yield return`语句,返回当前时钟位置p和数字n的配对`(p, n)`,即`(1, 4)`。此时,比赛表中的第五场比赛是1号选手对阵4号选手。
- 继续循环,进入第六次调用`yield return`语句。
- 计算左侧数字l,它等于p加上i,即2。
- 计算右侧数字r,它等于p加上(n-1)-i,即3。
- 使用`yield return`语句返回左侧数字l和右侧数字r的配对`(l, r)`,即`(2, 3)`。此时,比赛表中的第六场比赛是2号选手对阵3号选手。
- 更新时钟位置p,使其加上n/2,即2。此时,时钟位置p指向下一轮的起始位置。
- 回到步骤3,开始下一轮的配对。由于此时时钟位置p超过了(n-1),即4,所以将其减去(n-1),即得到1。此时,时钟位置p重新指向下一轮的起始位置。
- ...

这样,`yield return`语句会按需生成比赛对阵,每次调用时返回一个比赛对阵,并在下一次调用时从迭代器Enumerator上一次离开的地方继续执行。

现在,让我们来看看直接使用`return`的运行过程:

1. 初始化时钟位置p为1。
2. 直接使用`return`语句返回当前时钟位置p和数字n的配对`(p, n)`,即`(1, 4)`。此时,方法立即终止执行,并将配对`(1, 4)`作为方法的结果返回。这意味着只会生成一个比赛对阵,即1号选手对阵4号选手。

使用`return`语句会立即终止方法的执行,并将指定的值作为方法的结果返回。这意味着方法只会生成一个比赛对阵,并且无法再继续执行其他的代码。


注:FairCycle方法设计为时钟的模式是为了确保每个参赛球队都能在锦标赛中与其他球队公平地对抗一次。该方法的好处包括:

  1. 公平性:通过时钟的模式(所有球队对半匹配一次),每支球队都有机会与其他球队进行比赛,确保了比赛的公平性。没有球队会被遗漏或被偏好,每个球队都有相同的机会竞争。

  2. 竞争性:FairCycle方法确保了每支球队都能面对各种对手,包括实力强大的球队和实力较弱的球队。这种多样性的对手使得比赛更具竞争性,增加了球队之间的激烈程度。

  3. 简洁性:时钟的模式使得比赛安排更加简洁明了。每支球队在每轮比赛中都有一个确定的对手,没有重复或遗漏的情况,减少了混乱和错误的可能性。

  4. 可预测性:由于时钟的模式,每支球队都可以提前知道在每一轮比赛中将要面对的对手。这样,球队可以提前制定战术和策略,更好地准备比赛。

假设有8支球队参加一个锦标赛,并且FairCycle方法被用来安排比赛。FairCycle方法的时钟模式将确保每支球队都能与其他球队公平地对抗一次。

首先,我们将8支球队编号为A、B、C、D、E、F、G、H。根据FairCycle方法,比赛的安排如下:

第一轮: A vs B C vs D E vs F G vs H

第二轮: A vs C B vs D E vs G F vs H

第三轮: A vs D B vs C E vs H F vs G

第四轮: A vs E B vs F C vs G D vs H

第五轮: A vs F B vs E C vs H D vs G

第六轮: A vs G B vs H C vs E D vs F

第七轮: A vs H B vs G C vs F D vs E

通过这个例子,我们可以看到每支球队都与其他球队公平地对抗了一次。FairCycle方法的时钟模式确保了每支球队都有相同的机会竞争,没有球队被遗漏或被偏好。这种安排方式确保了比赛的公平性,并且每支球队都有机会面对各种对手,增加了比赛的竞争性。


测试用例:

 1 using NUnit.Framework;
 2 using System;
 3 using System.Linq;
 4 using System.Collections.Generic;
 5 
 6 namespace Solution {
 7   
 8   [TestFixture]
 9   public class SolutionTest
10   {
11     [Test]
12     public void Test2Teams()
13     {
14       var expected = new []{ new []{(1, 2)} };
15 
16       var actual = Tournament.BuildMatchesTable(2);
17       Assert.That(actual, Has.Length.EqualTo(1), "Should have 1 round");
18       Assert.That(actual[0], Has.Length.EqualTo(1), "The round should have 1 match");
19       if(actual[0][0].Item1>actual[0][0].Item2) (actual[0][0].Item1, actual[0][0].Item2) = (actual[0][0].Item2, actual[0][0].Item1);
20       Assert.AreEqual(expected, actual, "The match should be team 1 vs team 2");
21     }
22     
23     [Test]
24     public void Test4Teams() => TestTeams(4);
25     
26     [Test]
27     public void Test20Teams() => TestTeams(20);
28     
29     [Test]
30     public void TestRandom()
31     {
32       Random rand = new Random();
33       
34       TestTeams(2*(3+rand.Next(3)));
35       TestTeams(2*(6+rand.Next(4)));
36     }
37 
38     public void TestTeams(int numberOfTeams)
39     {
40       List<int> teamsExpected = Enumerable.Range(1, numberOfTeams).ToList();
41       HashSet<(int, int)> matchesExpected = new HashSet<(int, int)>();
42       foreach(var round in TournamentSolution.BuildMatchesTable(numberOfTeams))
43       {
44         foreach(var game in round) matchesExpected.Add(game.Item1>game.Item2?(game.Item2,game.Item1):(game.Item1, game.Item2));
45       }
46 
47       var actual = Tournament.BuildMatchesTable(numberOfTeams);
48       Assert.That(actual, Has.Length.EqualTo(numberOfTeams-1), $"Should have {numberOfTeams-1} rounds");
49       foreach(var round in actual)
50       {
51         List<int> teamsByRound = new List<int>();
52         Assert.That(round, Has.Length.EqualTo(numberOfTeams/2), $"Each round should have {numberOfTeams/2} matches");
53         foreach(var game in round)
54         {
55           Assert.That(game, Is.InstanceOf(typeof((int, int))), "Each match is a tupple of 2 teams");
56           teamsByRound.Add(game.Item1);
57           teamsByRound.Add(game.Item2);
58           Assert.True(matchesExpected.Remove(game.Item1>game.Item2?(game.Item2,game.Item1):(game.Item1, game.Item2)), $"{game} is a duplicate or doesn't exist");
59         }
60         teamsByRound.Sort();
61         Assert.AreEqual(teamsExpected, teamsByRound, "Each round should have matches with every team");
62       }
63       Assert.IsEmpty(matchesExpected, "At least one match isn't scheduled");
64     }
65   }
66 }
67 
68 public class TournamentSolution
69 {
70     public static (int, int)[][] BuildMatchesTable(int numberOfTeams)
71     {
72       List<int> teams = Enumerable.Range(1, numberOfTeams).ToList();
73       int roundsNbr = numberOfTeams-1, gamesNbr = numberOfTeams /2, rotatorID = roundsNbr-1, buffer = 0;
74       (int, int)[][] result = new (int, int)[roundsNbr][];
75 
76       for (int i=0; i<roundsNbr; i++)
77       {
78         result[i] = new (int, int)[gamesNbr];
79         
80         for (int j = 0; j < gamesNbr; j++) result[i][j] = (teams[0 + j], teams[roundsNbr - j]);
81 
82         buffer = teams[rotatorID];
83         teams.RemoveAt(rotatorID);
84         teams.Insert(0, buffer);
85       }
86       
87       return result;
88     }
89 }

 文章来源地址https://www.toymoban.com/news/detail-712203.html

到了这里,关于【解惑】孜孜不倦,用足球赛程详解c#中的yield return用法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C#中的ArrayList类详解

    目录 一、ArrayList类定义 二、ArrayList类与数组的区别 三、ArrayList类对象的声明 1、默认的构造器 2、用一个ICollection对象来构造 3、用指定的大小初始化内部的数组 四、ArrayList常用属性 五、ArrayList常用方法 1、ArrayList元素的添加  (1)Add()方法 (2)Insert()方法 (3)综合例 2、

    2024年02月07日
    浏览(40)
  • C#中的Hashtable(哈希表)详解

    目录 一、Hashtable(哈希表) 二、常用的Hashtable的构造函数 1、使用默认的初始容量 2、使用指定的初始容量 三、Hashtable常用属性 四、Hashtable元素的添加 五、Hashtable元素的删除 1、Clear()方法 2、Remove()方法 六、Hashtable的遍历 七、Hashtable元素的查找 1、Contains()方法 2、ContainsKe

    2024年02月13日
    浏览(43)
  • C#中的委托(Delegate)和事件 (Event)详解与使用范例

    最近天气晴雨不定,你因为害怕打游戏时忘记在下雨时收衣服或者在天晴时把衣服挂出去,于是你委托好友小明在天气发生变化时打电话通知你,这就是一种委托. 下面是这种委托的实例代码 以上代码的输出为 值得一提的是,如下代码为创建一个继承自Delegate类的名为XiaoMing的子类

    2023年04月12日
    浏览(42)
  • 掌握C#中的GUI多线程技巧:WinForms和WPF实例详解

      概述: 本指南详细解释了在C#中如何在创建控件的线程以外的线程中访问GUI。基础功能使用`Control.Invoke`(WinForms)或`Dispatcher.Invoke`(WPF),高级功能则利用`SynchronizationContext`实现线程间通信,确保代码清晰可读。 在C#中,要在创建控件的线程以外的线程访问控件,可以使用

    2024年03月19日
    浏览(36)
  • 程序人生 | 与足球共舞的火柴人(致敬格拉利什,赋予足球更深的意义)

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端 📃 个人状态: 在校大学生一枚,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力n年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2Vue3项目实战 🥝

    2024年02月02日
    浏览(46)
  • 一文解惑 | crm是什么意思啊

    CRM客户管理系统是一种十分重要的商业管理手段,它通过对大量客户信息的收集、分析和整理,来管理客户关系、提高客户满意度、增强客户忠诚度,从而实现企业的营销目标。 在市场竞争的日益激烈的今天,越来越多的企业开始关注 CRM客户管理系统的重要性 。因此,通过

    2024年02月11日
    浏览(39)
  • Python画3D足球2

    足球是正五边形和正六边形拼接而成,由此形成的骨架结构,可通过切割正二十面体获得,所以画足球的第一步是画正二十面体:Python绘制正二十面体 在学会绘制正二十面体之后,就可以通过切割顶点从而得到足球的骨架:用Python画一个足球 尽管通过对正二十面体削角得到

    2023年04月14日
    浏览(42)
  • Spring(九) - 解惑 spring 嵌套事务.2

        1. 事务传播特性             在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看,绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下 TransactionDefinition 接口中的定义 Java代码  我们可

    2024年02月14日
    浏览(49)
  • GPT所想即所见的快速解惑

    这是学习笔记的第 2464篇文章 在很多年前,自己脑袋里面冒出来这样的一段文字, 黑暗中一个孩子站在田野上看着另外一个孩子 就好像自己在看自己。 说是感慨也好,伤感也罢。那种画面感是一直难以名状的,最近试了下GPT,给我了一些灵感和部分答案。 画一幅画,黑暗

    2024年02月16日
    浏览(34)
  • 【代码分析】初学解惑C++:函数适配器

    一、函数适配器是什么? (1). 简单来说函数适配器就是基于原有函数功能的基础上,再增加一些功能。跟pointer like class有些相似,都是想在原有的功能上,做更多的事情。 (2). 适配器的意思就是将某些已经存在的东西进行限制或者组合变成一个新的东西,这个新的东西体现一

    2024年02月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包