N皇后问题的并行解法

2023-05-16

N皇后问题的并行解法

N皇后问题

其实就是一个n*n的棋盘上摆n个皇后,任意两个皇后不能同行,同列,同对角线,求出所有的摆法。这个问题可以看做是求n的组合数。比如第一列上的皇后在第x1行,第二列在x2行…..最后的一个解就是x1x2x3…xn。这是[1,n]的一个排列,求出全排列,然后去掉不符合的就是题目的解,而全排列的求解实际就是一个简答的DFS。但是这个方法太暴力了,实际上不少情况下不用摆到最后一列就知道这个解是无效的,这个时候就可以返回了。

单线程的解法

/*
singleThread.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#define MAXN 100
#define true 1
#define false 0
inline int abs(int a) {
    return a >= 0 ? a : (-a); 
}
int P[MAXN],used[MAXN],count,n;

void generateP(int index) {//index指[1-index-1]列已经选好皇后,现在从index列开始递归
    int flag;
    if(index == n + 1) {
        count++;
        return;
    }
    for(int x = 1; x <= n; x++) { //第x行
        if(used[x] == false) { //第x行还没有皇后
            flag = true; //flag表示当前行的皇后是否会和之前的皇后冲突,此处只需要计算是否在一条对角线上就可以了
            for(int pre = 1; pre< index; pre++) {
                if(abs(index - pre) == abs(x - P[pre])) {
                    flag = false;
                    break;
                }
            }
            if(flag) {
                P[index] = x;
                used[x] = true;
                generateP(index + 1);
                used[x] = false;
            }
        }
    }
}
int main(int argc,char **argv)
{
    if(argc != 2) {
        fprintf(stderr,"error args\n");
        return -1;
    }
    struct timeval start,end;
    n = atoi(argv[1]);
    long long startusec,endusec;
    gettimeofday(&start,NULL);
    generateP(1);
    gettimeofday(&end,NULL);
    printf("result count is %d\n",count);
    startusec = start.tv_sec * 1000000 + start.tv_usec;
    endusec = end.tv_sec * 1000000 + end.tv_usec;
    printf("spend %.4f s\n", (endusec - startusec)/1000000.0);
    return 0;

}

编译后测试运行

wd@ub-linux:Nhuanghou$ ./a.out 8
result count is 92
spend 0.0002 s
wd@ub-linux:Nhuanghou$ ./a.out 9
result count is 352
spend 0.0008 s
wd@ub-linux:Nhuanghou$ ./a.out 10
result count is 724
spend 0.0084 s
wd@ub-linux:Nhuanghou$ ./a.out 11
result count is 2680
spend 0.0228 s
wd@ub-linux:Nhuanghou$ ./a.out 12
result count is 14200
spend 0.1086 s
wd@ub-linux:Nhuanghou$ ./a.out 13
result count is 73712
spend 0.6085 s
wd@ub-linux:Nhuanghou$ ./a.out 14
result count is 365596
spend 3.8334 s
wd@ub-linux:Nhuanghou$ ./a.out 15
result count is 2279184
spend 25.2267 s

可以发现程序求解的速度是呈指数上升的。16皇后的解,难得等,就没做。。。。

如果这个题用多线程做,应该是有性能提升的吧?

并行的解法

以第一列为分解,先预定好第一列的皇后位置,然后将剩下的任务分发到各线程。但是cpu的核数有限,我的机器是4核,所以创建n个线程是没有意义的,创建4个线程较好。所以应该有一种机制能管理任务的分发,当一个线程任务做完了,能自动去接受剩下的计算任务。这个其实用线程池就好。然后就是对单线程程序的简单修改,将一些全局数据私有化。比如单线程中的P[]和used[],现在因为各个线程的计算任务并行执行,所以要为每个线程都设置P[]和used[]。(线程池的博客下次再写,会有详细注释)

上代码

#include "ThreadPool.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#define MAXN 100
#define true 1
#define false 0

struct Result {
    int xUsed;
    int count;
};
inline int abs(int a) {
    return a >= 0 ? a : (-a); 
}

int n; 
__thread Result *re;
__thread int P[MAXN],used[MAXN];

void generateP(int index/*,int *pCount,int *P,int* used*/) {
    int flag;
    if(index == n + 1) {
        re->count++;
        return;
    }
    for(int x = 1; x <= n; x++) {
        if(used[x] == false) {
            flag = true;
            for(int pre = 1; pre< index; pre++) {
                if(abs(index - pre) == abs(x - P[pre])) {
                    flag = false;
                    break;
                }
            }
            if(flag) {
                P[index] = x;
                used[x] = true;
                generateP(index + 1/*,pCount,P,used*/);
                used[x] = false;
            }
        }
    }
}

void thrFun(void *arg) {

    struct timeval start,end;
    long long startusec,endusec;
    re = (Result*)arg;
    // __thread int P[MAXN],used[MAXN];
     memset(P,0,sizeof(int)*MAXN);
     memset(used,0,sizeof(int)*MAXN);
    P[1] = re->xUsed;
    used[re->xUsed] = true;

    gettimeofday(&start,NULL);
    generateP(2/*,&(re->count),P,used*/);
    gettimeofday(&end,NULL);
    startusec = start.tv_sec * 1000000 + start.tv_usec;
    endusec = end.tv_sec * 1000000 + end.tv_usec;
    printf("spend %lld us\n", (endusec - startusec));
}


int main(int argc,char **argv)
{
    if(argc != 3) {
        fprintf(stderr,"error args\n");
        return -1;
    }
    int nThr;//工作线程数
    int sum = 0;
    struct timeval start,end;
    n = atoi(argv[1]);
    nThr = atoi(argv[2]);
    long long startusec,endusec;
    Result *reArray = (Result*)malloc(sizeof(Result)*n);
    memset(reArray,0,sizeof(Result)*n);

    gettimeofday(&start,NULL);

    {
        ThreadPool tp(nThr);
        for(int i = 0; i < n; i++) {
            reArray[i] = {i + 1,0};
            tp.addTask(thrFun,(void*)(&reArray[i]));
        }
    }
    for(int i = 0; i < n; i++) {
        sum += reArray[i].count;
    }
    gettimeofday(&end,NULL);

    printf("result = %d\n",sum);
    startusec = start.tv_sec * 1000000 + start.tv_usec;
    endusec = end.tv_sec * 1000000 + end.tv_usec;
    printf("spend %lld us\n", (endusec - startusec));
    return 0;
}

编译后测试,发现计算时间还是有不小的提升的。

wd@ub-linux:Nhuanghou$ ./a.out 15 4   #15皇后,4个工作线程
_worker ar 7010
_worker ar 7012
_worker ar 7009
_worker ar 7011
threadpool stopped
7010 get a task
7012 get a task
7009 get a task
7011 get a task
7010 get a task
7012 get a task
7009 get a task
7011 get a task
7010 get a task
7012 get a task
7009 get a task
7011 get a task
7010 get a task
7012 get a task
7009 get a task
all threads exit
result = 2279184
spend 10.5478 s

可以发现这次15皇后只用了10s。快了不少。

但是有一个问题是,如果这里面只有一个工作线程

result = 2279184
spend 37.4849 s

会发现多花了将近10s.这有点可怕,(⊙o⊙)…,现在还没找到原因。。。各位若是知道为什么,望告知。

后面用perf看了下具体的执行情况如下

(原始的单进程的情况,N=15)

result count is 2279184
spend 25.2779 s

 Performance counter stats for './a.out 15':

      25262.110738      task-clock (msec)         #    0.999 CPUs utilized          
               400      context-switches          #    0.016 K/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
                50      page-faults               #    0.002 K/sec                  
    93,273,276,839      cycles                    #    3.692 GHz                    
   142,353,387,972      instructions              #    1.53  insn per cycle         
    17,434,460,005      branches                  #  690.143 M/sec                  
     1,307,320,846      branch-misses             #    7.50% of all branches        

      25.278347191 seconds time elapsed

(多线程版,但是设置只有一个工作线程)

result = 2279184
spend 36645590 us

 Performance counter stats for './a.out 15 1':

      36645.398798      task-clock (msec)         #    1.000 CPUs utilized          
               115      context-switches          #    0.003 K/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
               110      page-faults               #    0.003 K/sec                  
   135,389,381,037      cycles                    #    3.695 GHz                    
   225,738,593,916      instructions              #    1.67  insn per cycle         
    42,698,239,331      branches                  # 1165.173 M/sec                  
     1,254,707,682      branch-misses             #    2.94% of all branches        

      36.647456082 seconds time elapsed

照理,这个指令数应该和上面那个差不太多,但是现在却相差20亿左右。都是单线程计算,为什么后面这个会比前面那个多出一倍的指令数?算法的问题?

线程池代码

#ifndef __THREADPOOL_H__   /*ThreadPool.h*/
#define __THREADPOOL_H__
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <sys/types.h>

#include <deque>
#include <vector>
typedef void (*CallBack)(void *);
struct Task
{
    CallBack func;
    void* arg;
};

class ThreadPool
{
public:
    ThreadPool(int size);
    ~ThreadPool();
    //void start();
    void addTask(CallBack f,void* arg);
private:
    static void* _worker(void* arg);
    bool _stopped;
    int  _size;
    pthread_cond_t _cond;
    pthread_mutex_t _mutex;
    std::deque<Task> _tasks;
    std::vector<pthread_t> _threadVec;
};
#endif
#include "ThreadPool.h" /*ThreadPool.cpp*/
void* ThreadPool::_worker(void* arg)
{
    ThreadPool *obj = (ThreadPool*)arg;
    pid_t tid = syscall(SYS_gettid);
    printf("_worker ar %d\n",tid);
    while(1)
    {
        pthread_mutex_lock(&obj->_mutex);
        while(obj->_tasks.empty() && !obj->_stopped)
        {
            pthread_cond_wait(&obj->_cond,&obj->_mutex);
            printf("%d get signal\n",tid);
        }
        Task t;
        if(!obj->_tasks.empty())
        {
            printf("%d get a task\n",tid);
            t = obj->_tasks.front();
            obj->_tasks.pop_front();
            pthread_mutex_unlock(&obj->_mutex);
            t.func(t.arg);
            continue;
        }
        break;
    }
    pthread_mutex_unlock(&obj->_mutex);
    return (void*)0;

}
ThreadPool::ThreadPool(int size)
{
    pthread_t tid;
    _size = size;
    _stopped = 0;
    _cond = PTHREAD_COND_INITIALIZER;
    _mutex = PTHREAD_MUTEX_INITIALIZER;
    for(int i = 0;i < size;i++)
    {
        pthread_create(&tid,NULL,ThreadPool::_worker,this);
        _threadVec.push_back(tid);
    }
}
ThreadPool::~ThreadPool()
{

    pthread_mutex_lock(&_mutex);
    _stopped = 1;
    printf("threadpool stopped\n");
    pthread_mutex_unlock(&_mutex);
    int threadSize = _threadVec.size();
    for(int i = 0; i < threadSize; i++)
    {
        pthread_join(_threadVec[i],NULL);
        pthread_cond_broadcast(&_cond);
    }
    printf("all threads exit\n");
}

void ThreadPool::addTask(CallBack f,void* arg)
{
    Task t = {f,arg};
    pthread_mutex_lock(&_mutex);
    _tasks.push_back(t);
    pthread_cond_signal(&_cond);
    pthread_mutex_unlock(&_mutex);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

N皇后问题的并行解法 的相关文章

  • ubuntu 驱动更新后导致无法进入界面

    问题描述 xff1a 安装新ubuntu系统后未禁止驱动更新导致无法进入登录界面 解决办法 xff1a 首先在进入BIOS中 xff0c 修改设置以进行命令行操作 xff0c 然后卸载已有的系统驱动 xff0c 最后安装新的驱动即可 开机按
  • PPC WM6.1智能手机上使用日语辞典浅谈

    在PPC手机上用日语辞典 xff08 広辞苑 xff0c 三省堂等 xff09 http bulo hjenglish com group topic 144804 PPC上的日文输入法 http bulo hjenglish com gr
  • PPC音量太小和听筒音太小的解决方法

    1下载注册表修改器 2复制修改器到PPC xff08 最好是卡上啦 xff09 3在PPC上运行修改器 我用的是华硕P525 以下是我小P的设置 xff1a 找到HKEY CURRENT USER ControlPanel Phone 项下
  • Delphi中的集成VBS脚本语言应用

    罗焱 从薇 王正浩 摘 要 xff1a 使用ActiveX Scripting技术 xff0c 可以在应用程序中集成使用脚本语言 本文介绍如何应用这一技术在Delphi应用程序中添加VBScript支持 关键词 xff1a ActiveX脚
  • python中嵌入C语言脚本

    借助Cinpy 和C语言解释器TinyCC xff0c 可以在python 程序里面直接嵌入C语言片断 不经编译直接使用C编写的函数了 win2k平台上 xff0c 简单的测试对比数据如下 xff08 递归方法计算第四十项兔子数列fib x
  • 最新Ubuntu内网源部署方法(Ubuntu20、Ubuntu21)

    最新Ubuntu内网源部署方法 1 下载公网离线源 安装apt mirror xff0c 并修改 etc apt mirror list内容 xff0c 以ubuntu20 04 为例说明 xff1a span class token co
  • wsl2-ubuntu安装图形界面;windows安装miniconda

    一 wsl2 ubuntu安装图形界面 1 安装 xff1a wsl2安装ubuntu Download VcXsrv Windows X Server from SourceForge net 2 配置 xff08 链接wsl和VcXsr
  • 双曲函数系列

    定义 双曲函数 xff08 Hyperbolic Function xff09 包括下列六种函数 xff1a sinh 双曲正弦 xff1a sinh x 61 e x e x 2 cosh 双曲余弦 xff1a cosh x 61 e x
  • 虚拟机增加磁盘空间(VMware虚拟机)

    1 写在前面 对于VMware虚拟机 xff0c 经常有最初分配的磁盘空间大小最后不够用的情况 xff0c 因此需要我们增加磁盘空间 网上看了一些博客资料 xff0c 大多不能完全照着做完 xff0c 参照了几个才实现 xff0c 2 操作
  • openSUSE通过snapper恢复系统

    事由 xff1a 系统中存在两个版本python xff0c 导致程序无法找到pygtk xff0c 遂打算删除所有python重新安装 xff0c KDE 崩溃只能用控制台 过程 xff1a 1 查找控制台安装kde的方法 xff0c 未
  • Scrapy 中 ImagesPipeline 无法执行,不起作用,图片无法下载的原因!

    经过检查scrapy样例程序的执行情况 xff0c 发现如下警告信息 xff1a 2021 04 11 22 33 59 scrapy middleware WARNING Disabled PianImgPipeline ImagesPi
  • 移植jpeg库文件到ARM开发板

    我们知道 xff0c jpeg格式的图片是经过压缩处理的 xff0c 所以想要在ARM开发板中显示 xff0c 就需要一些库文件的支持 xff0c 当然牛逼的人也可以自己写解压算法做库文件 xff0c 不过作为小白的我 xff0c 还是先借
  • 设置系统隐藏文件 + 恢复显示隐藏文件的CMD命令!

    打开记事本输入以下内容 for f 34 delims 61 34 i in 39 dir a b 39 do attrib 34 i 34 43 s 43 h 43 r 43 a 保存为 隐藏 bat xff08 扩展名一定要改 xff0
  • 调整tty文本模式终端字体大小

    以debian为例 xff0c 介绍两种设置方法 1 通过console tools设置控制台字体 1 1 选用并测试合适的字体和字库文件 xff1a dell ls usr share consolefonts 1 2 测试选用喜爱的字库
  • sudo apt-get install wine 速度非常慢的解决方法

    经过我的测试 xff0c 在有的ubuntu系统中 xff0c 比如linux mint中 xff0c 我发现 sudo apt get install wine 的速度平均以b s为单位进行传输 xff0c 这样的速度非常的慢 xff0c
  • 使用LIOS之中遇到的问题

    1 运行train tassdata后的第一个问题 xff1a language combobox changed Started Exception in thread Thread 3 Traceback most recent cal
  • WIN10 anaconda 安装 tensorflow-gpu不出错的最佳解决办法(不在系统安装CUDA)

    来源 xff1a https www pugetsystems com labs hpc The Best Way to Install TensorFlow with GPU Support on Windows 10 Without I
  • PostgreSQL教程

    一 PostgreSQL介绍 PostgreSQL是一个功能强大的 开源 的关系型数据库 底层基于C实现 PostgreSQL的开源协议和Linux内核版本的开源协议是一样的 BDS协议 xff0c 这个协议基本和MIT开源协议一样 xff
  • you-get的安装与使用

    youget简介 you get是github上python的一个开源库 https github com soimort you get xff0c 使用you get你只需要取得视频所在网页链接地址就可以很轻松的下载下来 xff0c 目
  • VS Code搭建PYQT5环境并创建Helloworld实例

    使用Python pip安装PyQt5和PyQt5 tool pip install PyQt5 pip install PyQt5 tools 在VS code中安装插件PYQT Integration 配置PYQT Integratio

随机推荐

  • ubuntu Xrdp远程连接Authentication is required to create a color managed device

    问题 Gnome Bug xff1a 无法点击 永不消逝的授权对话框 解决 xff1a https blog csdn net wu weijie article details 108481456
  • Linux运维入门~21.系统磁盘管理,解决u盘连接电脑无反应,解决卸载u盘正忙问题

    本节我们来了解一下linux系统的磁盘管理 识别设备常用命令有 xff1a fdisk l 查看真实存在的设备 cat proc partition 系统识别的设备 blkid 系统可使用的设备 df 系统正在挂载的设备 du 查看磁盘容量
  • 快速幂取模:求 a^b % N(C++)

    在某些情况下 xff0c 我们需要求模 N 情况下某个数的多次幂 xff0c 例如 xff1a 求多次幂结果的最后几位数 RSA算法的加解密 如果底数或者指数很大 xff0c 直接求幂再取模很容易会出现数据溢出的情况 xff0c 产生错误的
  • 新手教程:手把手教你使用Powershell批量修改文件名

    适合完全没用过 xff0c 没了解过powershell的人 1 打开Windows Powershell ISE 在任务栏搜索框中输入ISE xff0c 然后打开 xff08 我的任务栏放在右边了 xff0c 所以是这个样子 xff09
  • Mac OS 开机密码重置

    通过 Mac OS 恢复功能启动 Apple 芯片 xff1a 将 Mac 开机并继续按住电源按钮 xff0c 直至看到启动选项窗口 选择标有 选项 字样的齿轮图标 xff0c 然后点按 继续 Intel 处理器 xff1a 将 Mac 开
  • 表达式求值:从“加减”到“带括号的加减乘除”的实践过程

    本文乃Siliphen原创 xff0c 转载请注明出处 xff1a http blog csdn net stevenkylelee 为什么想做一个表达式求值的程序 最近有一个需求 xff0c 策划想设置游戏关卡的某些数值 xff0c 这个
  • Linux中source命令,在Android build 中的应用

    source命令 xff1a source命令也称为 点命令 xff0c 也就是一个点符号 xff08 xff09 source命令通常用于重新执行刚修改的初始化文件 xff0c 使之立即生效 xff0c 而不必注销并重新登录 用法 xff
  • Edge 错误代码: STATUS_ACCESS_DENIED 解决方案

    1 到C盘Edge的文件全部删掉 2 到电脑管家的软件管理重新下载Edge 或者 去官网下载 3 再次打开Edge xff0c 功能都回来了 注 xff1a 该解决方案源自于edge吧的四川男篮大佬
  • centos7.9离线安装mysql5.7

    前言 windows server2003服务器 xff0c 安装mysql提示需要net framework xff0c 费了半天劲装好了 xff0c 发现解压版redis无法启动 xff0c 换了个低版本也是无法安装 xff0c 服务器
  • vs2019-slicer编译问题记录

    3D Slicer编译过程问题记录 官网教程 xff1a https slicer readthedocs io en latest developer guide build instructions windows html 环境 CM
  • sudo npm command not found 问题解决

    这种情况通常是使用 npm 命令可以正常使用 xff0c 但使用sudo npm 命令便会报 command not found 这是什么原因呢 xff1f 输入which npm可以得到 usr local bin npm xff0c 这
  • (POJ1201)Intervals <差分约束系统-区间约束>

    Intervals Description You are given n closed integer intervals ai bi and n integers c1 cn Write a program that reads the
  • 【sv与c】sv与c交互

    网上此类文章很多 xff0c 这里暂时不放具体实现和测试结果 xff0c 后续持续更新 下面引用一些帖子 xff0c 帖子中涉及到具体做法 vcs联合编译v sv c 43 43 代码 sxlwzl的专栏 CSDN博客 1 xff0c 假设
  • stm32f103c8t6最小系统

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言stm32f103c8t6构成二 xff1a 电源电路稳压模块注意 复位电路NRST 时钟电路程序下载电路JTAGSWD 启
  • udp中的connect()&bind()

    connect amp bind 的作用 udp udp connect span class hljs preprocessor include lt sys types h gt span span class hljs preproc
  • Linux Hook技术实践

    LInux Hook技术实践 什么是hook 简单的说就是别人本来是执行libA so里面的函数的 xff0c 结果现在被偷偷换成了执行你的libB so里面的代码 xff0c 是一种替换 为什么hook 恶意代码注入调用常用库函数时打lo
  • Tensorflow Cnn mnist 的一些细节

    Tensorflow cnn MNIST 笔记 写这个完全是记录看官网example时不懂 xff0c 但后来弄懂的一些细节 当然这个可以算是对官方文档的补充 xff0c 也许每个人遇到的不懂都不一样 xff0c 但希望对大家有帮助 先上代
  • effective cpp 读书笔记2

    当你用到多态时 xff0c 务必把析构函数做成虚函数 以前知道子类的析构函数会自动调用父类的析构函数 xff0c 然而今天却发现不总是这样 当你用基类的指针指向派生类时 xff0c 然后delete这个指针时 xff0c 有可能就不会调用派
  • malloc与缺页的一些的时间测量

    malloc与缺页的一些的时间测量 时间函数 span class hljs keyword struct span timespec time t tv sec span class hljs keyword long span span
  • N皇后问题的并行解法

    N皇后问题的并行解法 N皇后问题 其实就是一个n n的棋盘上摆n个皇后 xff0c 任意两个皇后不能同行 xff0c 同列 xff0c 同对角线 xff0c 求出所有的摆法 这个问题可以看做是求n的组合数 比如第一列上的皇后在第x1行 xf