如何避免两个句子,第一个字母相同,在一个洗牌中彼此相邻?

我有一个我编写的代码,它会对音乐曲目的文本文件进行随机播放,如何调整我的代码,以便每次运行程序时,都不会有两首以相同的第一个字母开头的曲目。 例如,艺术家Hozier的两首曲目不应该彼此相邻。

正确:

Hozier - Take Me To Church Pink - So What Hozier - Cherry Wine 

不正确:

 Hozier - Take Me To Church Hozier - Cherry Wine Pink - So What 

这是我的代码:

 #include  #include  #include  #include  // Accepts: command line input // Returns: 0 if no error int main(int num_args, char *arg_strings[]) { int x = 0, i, track_count = 0; unsigned long Max_Length = 0; char line[500], *temp; FILE *file = fopen("InputFiles/playlist.txt", "r" ); /* The next line checks if the playlist file exists and if it's not there, "Cannot Open File" is printed to the screen */ if (file == NULL){ printf("Cannot open file\n"); } /* The following code identifies each line in the text and lines are shuffled accordingly */ while (fgets(line, sizeof(line), file) != NULL) { track_count++; if (strlen(line) > Max_Length) Max_Length = strlen(line); } rewind(file); char *Array[track_count]; while (fgets(line, sizeof(line), file) != NULL) { Array[x] = malloc(strlen(line)); if (Array[x] == NULL){ printf("A memory error occurred.\n"); return(1); } strcpy(Array[x], line); /* change \n to \0 */ Array[x][strlen(Array[x])-1] = '\0'; x++; } printf("The original playlist is:\n"); for (x = 0; x = 0; x--){ i = (int) rand() % track_count; temp = Array[x]; Array[x] = Array[i]; Array[i] = temp; } printf("\nShuffled Array\n"); for (x = 0; x < track_count; x++) printf("%2d %s\n", x, Array[x]); return 0; } 

如果允许重复相同的歌曲(例如,当音乐播放器同时播放和重复播放时),您可以只记住前一个第一个字母并从那些没有相同的第一个字母的那些中随机选择每个连续的歌曲。上一首歌。

然而,为了不重复地对歌曲进行洗牌,仅考虑最后一个位置是不起作用的,例如,如果你的歌曲有第一个字母CACBCAC ,它们最终可能会成为ABACCCC ,你只剩下C -songs。 您可以检测到这种情况(即,不以前一首字母开头的未洗过的歌曲的数量为零),并且在这些情况下,找到可以插入每首新歌的先前排序列表中的位置,并从那些中随机挑选。 例如,如果你有上面的第一个字母并且在ABAC ,那么以C开头的下一首歌可以被插入3个不同的位置( CABACACBACABCAC )。

使用256个桶子列表。

如果可以的话,我会稍后尝试编码。 认为该算法值得发布。

伪代码:

  1. n数据输入256个列表中的1个列表Array[first letter][]

  2. 将每个非空列表放在优先级队列中,如步骤3所示

  3. 根据列表中最大的项目数将列表放入列表的优先级队列中。

  4. 从队列中提取最大列表,称之为G1 。 从该列表中删除1项并放入ArrayB[]

  5. 从队列中提取最大列表,称之为G2 。 从该列表中删除1项并放入ArrayB[]

  6. 如果不为空,则将G1重新放入优先级队列。

  7. G2现在变成了G1

  8. 继续步骤5,直到队列为空。

  9. 继续步骤7直到G2为空。

  10. 此时我们现在有一个列表ArrayB[] ,尽管不是随机的,但如果可能的话,它符合标准。

  11. 通过ArrayB[] ,比如3*n次,如果交换符合无重复条件,则随机交换一对itmes。

我想出这样的,似乎工作但想法需要改进(如果所有曲目都以相同的字母开头怎么办?可以添加一些尝试限制)

 for (x = track_count - 2; x > 1; x--){ while( 1 ) { i = rand() % ( track_count - 1 ) + 1; if( Array[x+1][0] == Array[i][0] ) continue; if( Array[x-1][0] == Array[i][0] ) continue; if( Array[i+1][0] == Array[x][0] ) continue; if( Array[i-1][0] == Array[x][0] ) continue; temp = Array[x]; Array[x] = Array[i]; Array[i] = temp; break; } } 

以下函数“isValidOrder”检测歌曲是否以有效顺序混洗。 它使用序列“ – ”作为艺术家的分隔符。 根据您的表现,如果检测到无效订单或者可能想要修改此代码,您可能只想再次播放歌曲。 像这样使用它:“isValidOrder(Array,track_count)”。 如果订单有效,则返回1 – 否则为0。

 int isSameArtist(char * artist1, int artist1Length, char * artist2, int artist2Length){ if (artist1Length != artist2Length) { return 0; } for (int i = 0; i < artist1Length; i++) { if (artist1[i] != artist2[i]) { return 0; } } return 1; } int getLengthOfArtist(char * title){ int length = 0; while (*title != '\0') { if(title[0] == ' ' && title[1] != '\0' && title[1] == '-' && title[2] != '\0' && title[2] == ' '){ return length; }else{ title ++; length ++; } } return length; } int isValidOrder(char ** Array, int track_count){ if (track_count == 0) return 1; char * artist = Array[0]; int artistLength = getLengthOfArtist(artist); for (int i = 1; i < track_count; i++) { char * nextArtist = Array[i]; int nextArtistLength = getLengthOfArtist(nextArtist); if (isSameArtist(artist, artistLength, nextArtist, nextArtistLength) == 1) { return 0; } artist = nextArtist; artistLength = nextArtistLength; } return 1; }