Hash表(C++)

        本篇将会开始介绍有关于 unordered_map 和 unordered_set 的底层原理,其中底层实现其实就是我们的 Hash 表,本篇将会讲解两种 Hash 表,其中一种为开放定址法,另一种为 hash 桶,在unordered_map 和 unordered_set 的底层实现中主要是使用的是 hash 桶。

        本篇分别介绍了 hash 表的两种实现方法,接着讲解 hash 冲突、扩容与 hash 函数的设计原则。最后使用代码实现 hash 表的数据结构。

目录

hash概念

1. 开放定址法

2. hash 桶

hash冲突与hash函数

1. hash冲突

2. hash 函数

3. hash 表的扩容

代码实现

1. 开放定址法

2. hash 桶

hash概念

        我们在顺序结构或者平衡树中,想要查找一个数必须经过关键码的多次比较,顺序查找的时间复杂度为O(n),平衡树的查找时间复杂度为O(logn)。但是是否存在一种搜索方法,我们可以不经过任何比较,直接从表中找到我们想要的元素

        我们构建的 hash 表就可以达到这样的效果:通过某种函数(hashfunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么我们在查找时通过该函数可以很快的找到该元素

        构造出相应的结构:

        插入元素:根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此方法进行存放。

        搜索元素:对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取出元素比较,若关键码相同,则搜索成功。

        本篇一共构造两种结构:开放定址法、hash 桶。如下:

1. 开放定址法

        关于开放定址法,其中的主要思想为:构建一个存放数据的 hash 表,然后通过 hashfunc 计算出元素对应的关键码,然后将元素对应的放置在相应的 hash 表中的位置,如下:

        假设我们存放元素集合:{1,7,6,4,5,9}。

        当我们想要进行搜索元素的时候,我们就可以直接使用对应的 hash 函数搜索对应的元素。

2. hash 桶

        关于 hash 桶,其主要思想为:我们首先先建立一个 hash 表,在 hash 表中的每一个位置,我们都直接将使用哈希函数计算出元素对应的关键码存放到对应位置,如下:

        假设我们存放的元素集合为:{1,7,6,4,5,9,44}。

        当其中同一个位置存在多个元素的时候,我们只需要将多的元素链接到最后元素的后面即可。

hash冲突与hash函数

1. hash冲突

        在实现 hash 表,计算元素对应的关键码的时候,难免会遇到关键码相同的情况,也就是同一个位置将会存放两个元素,也就是 hash 冲突。对于开放定址法而言就会存在 hash 冲突,而 hash 桶则不用担心会出现 hash 冲突

        关于 hash 冲突的解决方法:

        线性探测:从发生冲突的位置开始,依次向后探测、直到寻找到下一个空位置为止。如下:

        插入:首先通过哈希函数获取插入元素在哈希表中的位置,若该位置没有元素则直接插入新的元素,若该位置中有元素发生哈希冲突,我们则使用线性探测找到下一个空位置,然后在插入元素,如下:

        删除:关于使用线性探测法删除元素,我们不能直接删除元素,直接删除元素将会影响其他元素的搜索。比如删除元素4,如果直接将其删掉,44的查找将会受到影响,所以线性探测采用标记的伪删除法来删除一个元素

enum State {EMPTY, EXIST, DELETE};
// EMPTY此位置为空
// EXIST此位置存在元素
// DELETE此位置元素已经删除

        二次探测:线性探测的缺陷是产生冲突的数据会堆积在一块,这与查找下一个空位置有关系,因为找空位置的方法就是挨着往后逐个去找,因此二次探索为了避免该问题,找到下一个空位置的方法为:Hi = (H0 + i ^ 2) % m 或者 Hi = (H0 - i ^ 2) % m,其中 H0 为通过散列函数计算出的关键码对应的位置,m 是表的大小。

2. hash 函数

        关于引起 hash 冲突的一个原因为:hash 函数的设计不合理。

        关于 hash 函数的设计原则有

        1. hash 函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有 m 个地址的时候,其值域必须在 0 ~ m - 1之间。

        2. hash 函数计算出来的地址能均匀分布在整个空间中。

        3. hash 函数的设计应该比较简单。

        常见的 hash 函数:直接定址法除留余数法、平方取中法、折叠法、随机数法……

        直接定址法:取关键字的某个线性函数为散列地址:hash(key) = A * key + B。直接定址法的优点为:简单且均匀;缺点为:需要事先知道关键字的分布情况。适合查找比较小且连续的情况。

        除留余数法:设散列表中允许的地址数为 m,取一个不大于 m,但最接近或者等于 m 的质数 p 作为除数,按照 hash 函数:hash(key)= key % p,将关键码转换成哈希地址。

        对于其他的 hash 函数不是很常用,便不在介绍。

3. hash 表的扩容

        虽然在使用 hash 表存储元素的时候,我们可以将元素不断的映射到 hash 表中来,但是我们的 hash 表也会存在存满的情况(使用开放定址法下的 hash 表),那么也就会存在一个 hash 表扩容的问题。

        hash 表的载荷因子定义为:α = 填入表中的元素个数 / hash 表的长度。

        关于 α 的取值,对于开放定址法而言,一般取值为0.7 ~ 0.8 以下。当表中的载荷因子大于 α 的时候,我们就需要对我们的 hash 表进行扩容。

代码实现

1. 开放定址法

        该代码的实现原理为开放定址法,如下:

#pragma once
#include <vector>
#include <string>

using namespace std;

template <class K>
struct HashFunc {
	size_t operator()(const K& key) {
		return (size_t)key;
	}
};

template<>
struct HashFunc <string> {
	size_t operator()(const string& key) {
		size_t hash = 0;
		for (auto e : key)
			hash = hash * 131 + e;
		return hash;
	}
};

// 开放定值法
namespace Open_Address {
	enum status { 
		EMPTY, DELETE, EXIST 
	};

	template <class K, class V>
	struct HashData {
		pair<K, V> _kv;
		status _status;

		HashData() = default;

		HashData(const pair<K,V>& data)
			: _kv(data)
		{}
	};

	template <class K, class V, class Hash = HashFunc<K>>
	class HashTable {
		typedef HashData<K, V> Data;
	public:
		// 构造函数
		HashTable() 
			: _n(0)
		{
			// 先开十个空间
			_tables.resize(10);
		}

		bool insert(const pair<K, V>& data) {
			if (find(data.first))
				return false;
			Hash hf;
			if (10 * _n / _tables.size() >= 7) {
				// 扩容
				HashTable newTable;
				newTable._tables.resize(2 * _tables.size());
				for (size_t i = 0; i < _tables.size(); i++) {
					if (_tables[i]._status == EXIST)
						newTable.insert(_tables[i]._kv);
				}
				_tables.swap(newTable._tables);
			}
			size_t cur = hf(data.first);
			cur %= _tables.size();

			while (_tables[cur]._status == EXIST) {
				cur++;
				cur %= _tables.size();
			}
			_n++;
			_tables[cur] = Data(data);
			_tables[cur]._status = EXIST;
			return true;
		}

		HashData<K, V>* find(const K& key) {
			Hash hf;
			size_t cur = hf(key);
			cur %= _tables.size();
			while (_tables[cur]._status != EMPTY) {
				if (_tables[cur]._status != DELETE && _tables[cur]._kv.first == key)
					return &_tables[cur];
				cur++;
				cur %= _tables.size();
			}
			return nullptr;
		}

		//bool erase(const K& key) {
		//	Hash hf;
		//	size_t cur = hf(key);
		//	cur %= _tables.size();
		//	if (find(key) == false)
		//		return false;
		//	while (_tables[cur]._kv.first != key) {
		//		cur++;
		//		cur %= _tables.size();
		//	}
		//	_tables[cur] = Data();
		//	_tables[cur]._status = DELETE;
		//	_n--;
		//	return true;
		//}

		bool erase(const K& key) {
			Data* Fd = find(key);
			if (Fd) return false;
			*Fd = Data();
			Fd->_status = DELETE;
			_n--;
			return true;
		}
	private:
		vector<Data> _tables;
		size_t _n;
	};
}

        关于此代码的实现,我们首先先实现计算元素关键码的类,其中主要包括两种元素,一种为数字类元素,另一类为字符串元素(计算字符串元素的关键码存在多种方式,本篇实现的只是其中的一种)。我们只需要使用一个类模板就可以实现计算关键码的类,其中字符串元素类的计算为特化的类,如下:

template <class K>
struct HashFunc {
	size_t operator()(const K& key) {
		return (size_t)key;
	}
};

// 模板特化
template<>
struct HashFunc <string> {
	size_t operator()(const string& key) {
		size_t hash = 0;
		for (auto e : key)
			hash = hash * 131 + e;
		return hash;
	}
};

        对于 hash 表的建立,我们还需要建立它的数据类型和状态类型(用来表征当前位置的状态),如下:

enum status { 
	EMPTY, DELETE, EXIST 
};

template <class K, class V>
struct HashData {
	pair<K, V> _kv;
	status _status;

	HashData() = default;

	HashData(const pair<K,V>& data)
		: _kv(data)
	{}
};

        接着是插入函数,其中插入函数主要的实现为判断当前是否需要扩容,以及计算出元素的关键码,然后将元素放入到对应的位置。如下:

bool insert(const pair<K, V>& data) {
	if (find(data.first))
		return false;
	Hash hf;
	// 判断是否需要扩容
	if (10 * _n / _tables.size() >= 7) {
		// 扩容
		HashTable newTable;
		newTable._tables.resize(2 * _tables.size());
		for (size_t i = 0; i < _tables.size(); i++) {
			if (_tables[i]._status == EXIST)
				newTable.insert(_tables[i]._kv);
		}
		_tables.swap(newTable._tables);
	}
	// 计算关键码
	size_t cur = hf(data.first);
	cur %= _tables.size();

	// 若当前位置存在元素,则放在下一个位置
	while (_tables[cur]._status == EXIST) {
		cur++;
		cur %= _tables.size();
	}
	_n++;
	_tables[cur] = Data(data);
	_tables[cur]._status = EXIST;
	return true;
}

        对于查找函数,我们需要计算出搜索元素的关键码,然后使用在表中不断的查找,若找到的位置为 EMPTY,则查找失败,若找到位置为 EXIST,则说明我们找到相应的元素了,如下:

HashData<K, V>* find(const K& key) {
	Hash hf;
	size_t cur = hf(key);
	cur %= _tables.size();
	while (_tables[cur]._status != EMPTY) {
		if (_tables[cur]._status != DELETE && _tables[cur]._kv.first == key)
			return &_tables[cur];
		cur++;
		cur %= _tables.size();
	}
	return nullptr;
}

        最后则是我们的删除元素,我们首先需要使用查找函数,查看是否能找到对应的元素,若找不到则直接返回 false(表示删除失败),若找到,直接将对应位置的状态设置为 DELETE,如下:

bool erase(const K& key) {
	Data* Fd = find(key);
	if (Fd) return false;
	*Fd = Data();
	Fd->_status = DELETE;
	_n--;
	return true;
}

2. hash 桶

        接下来的代码为实现 hash 桶的代码,如下:

template <class K>
struct HashFunc {
	size_t operator()(const K& key) {
		return (size_t)key;
	}
};

template<>
struct HashFunc <string> {
	size_t operator()(const string& key) {
		size_t hash = 0;
		for (auto e : key)
			hash = hash * 131 + e;
		return hash;
	}
};

// hash桶
namespace Hash_Bucket {
	template<class K, class V>
	struct HashNode {
		pair<K, V> _kv;
		HashNode* next = nullptr;

		HashNode() = default;
		HashNode(const pair<K,V>& kv)
			: _kv(kv)
			, next(nullptr)
		{}
	};

	template <class K, class V, class Hash = HashFunc<K>>
	class HashTable {
		typedef HashNode<K, V> Node;
	public:
		HashTable() {
			_n = 0;
			_tables.resize(10, nullptr);
		}

		// 拷贝构造函数
		HashTable(const HashTable<K, V>& ht) {
			_tables.resize(ht._tables.size());
			size_t size = ht._tables.size();
			for (size_t i = 0; i < size; i++) {
				Node* cur = ht._tables[i];
				while (cur) {
					Node* next = cur->next;
					// 插入元素
					insert(cur->_kv);
					cur = next;
				}
			}
		}


		~HashTable() {
			for (size_t i = 0; i < _tables.size(); i++) {
				Node* cur = _tables[i];
				if (cur != nullptr) {
					Node* next = cur->next;
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}

		bool insert(const pair<K, V>& kv) {
			if (find(kv.first))
				return false;
			// 扩容
			Hash hf;
			//if (_n == _tables.size()) {
			//	// 扩容的时候,只需要转换一整只即可
			//	HashTable newTable;
			//	newTable._tables.resize(2 * _tables.size());
			//	for (size_t i = 0; i < _tables.size(); i++) {
			//		if (_tables[i] != nullptr) {
			//			//Node* cur = _tables[i];
			//			//while (cur) {
			//			//	newTable.insert(cur->_kv);
			//			//	cur = cur->next;
			//			//}
			//			
			//			// 将所有节点取下来连接上去
			//			Node* cur = _tables[i];
			//			while (cur) {
			//				Node* next = cur->next;
			//				// 将节点取下来重新加到新表上
			//				size_t index = hf(cur->_kv.first) % newTable._tables.size();
			//				cur->next = newTable._tables[index];
			//				newTable._tables[index] = cur;
			//				cur = next;
			//			}
			//		}
			//	}
			//	_tables.swap(newTable._tables);
			//}

			if (_n == _tables.size()) {
				vector<Node*> newTables;
				newTables.resize(_tables.size() * 2, nullptr);
				size_t size = _tables.size();
				for (int i = 0; i < size; i++) {
					Node* cur = _tables[i];
					while (cur) {
						Node* next = cur->next;
						// 将节点取下来放在新表上
						size_t index = hf(cur->_kv.first) % newTables.size();
						cur->next = newTables[index];
						newTables[index] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}
				_tables.swap(newTables);
			}

			size_t index = hf(kv.first);
			index %= _tables.size();
			Node* newNode = new Node(kv);
			newNode->next = _tables[index];
			_tables[index] = newNode;
			_n++;
			return true;
		}

		Node* find(const K& key) {
			Hash hf;
			size_t index = hf(key);
			index %= _tables.size();
			Node* cur = _tables[index];
			if (cur == nullptr)
				return nullptr;
			while (cur) {
				if (cur->_kv.first == key)
					return cur;
				cur = cur->next;
			}
			return nullptr;
		}

		bool erase(const K& key) {
			if (find(key) == nullptr)
				return false;
			Hash hf;
			size_t index = hf(key);
			index %= _tables.size();
			Node* cur = _tables[index];
			Node* prev = _tables[index];
			if (cur->_kv.first == key) {
				_n--;
				_tables[index] = cur->next;
				delete prev;
				return true;
			}
			while (cur->_kv.first != key) {
				if (cur->_kv.first == key) {
					prev->next = cur->next;
					delete cur;
					_n--;
					return true;
				}
				prev = cur;
				cur = cur->next;
			}
			return false;
		}

	private:
		vector<Node*> _tables;
		size_t _n;
	};
}

        对于计算关键码的类,我们仍然沿用开放定址法的计算方法。

        但是对于 hash 表中的元素,我们则是采用指针的方法存放对应的元素,并将其初始化为 nullptr,表示当前位置没有元素。所以对于插入元素的设计,我们则将其设计为结点,当插入元素时,只需要连接到对应位置即可。

template<class K, class V>
struct HashNode {
	pair<K, V> _kv;
	HashNode* next = nullptr;

	HashNode() = default;
	HashNode(const pair<K,V>& kv)
		: _kv(kv)
		, next(nullptr)
	{}
};

        接下来是插入元素,对于需要插入的元素,我们仍然是需要先判断该 hash 表是否需要扩容(这里的扩容和开放定址法的扩容不一样,元素个数和容量一致的时候我们才扩容)。然后通过计算关键码找到需要插入的位置,如下:

bool insert(const pair<K, V>& kv) {
	if (find(kv.first))
		return false;
	// 扩容
	Hash hf;

	// 扩容
	if (_n == _tables.size()) {
		vector<Node*> newTables;
		newTables.resize(_tables.size() * 2, nullptr);
		size_t size = _tables.size();
		for (int i = 0; i < size; i++) {
			Node* cur = _tables[i];
			while (cur) {
				Node* next = cur->next;
				// 将节点取下来放在新表上
				size_t index = hf(cur->_kv.first) % newTables.size();
				cur->next = newTables[index];
				newTables[index] = cur;

				cur = next;
			}
			// 将原 hash 表对应位置设置为null
			_tables[i] = nullptr;
		}
		_tables.swap(newTables);
	}
	size_t index = hf(kv.first);
	index %= _tables.size();
	Node* newNode = new Node(kv);
	newNode->next = _tables[index];
	_tables[index] = newNode;
	_n++;
	return true;
}

        接下来是查找函数,此查找函数就较为简单,因为直接就可以通过计算关键码查询是否能找到该元素。如下:

Node* find(const K& key) {
	Hash hf;
	size_t index = hf(key);
	index %= _tables.size();
	Node* cur = _tables[index];
	if (cur == nullptr)
		return nullptr;
	while (cur) {
		if (cur->_kv.first == key)
			return cur;
		cur = cur->next;
	}
	return nullptr;
}

        最后是删除元素函数,仍然是先查找元素是否在 hash 表中,查找到之后在进行删除,其中会分为删除元素的位置在当前 hash 表的最后链接位置和中间位置进行分别讨论,如下:

bool erase(const K& key) {
	if (find(key) == nullptr)
		return false;
	Hash hf;
	size_t index = hf(key);
	index %= _tables.size();
	Node* cur = _tables[index];
	Node* prev = _tables[index];
	if (cur->_kv.first == key) {
		_n--;
		_tables[index] = cur->next;
		delete prev;
		return true;
	}
	while (cur->_kv.first != key) {
		if (cur->_kv.first == key) {
			prev->next = cur->next;
			delete cur;
			_n--;
			return true;
		}
		prev = cur;
		cur = cur->next;
	}
	return false;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/797825.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

6、evil box one

低—>中 目标&#xff1a;获取root权限以及2个flag 主机发现 靶机 192.168.1100.40 或者使用fping -gaq 192.168.100.1/24发现主机使用ping的方式。 端口扫描 发现开放了22和80 可以使用-A参数&#xff0c;-A参数会得到更多的扫描细节 访问80端口就是一个apache的基本的…

Redis 7.x 系列【23】哨兵模式

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 工作原理2.1 监控2.2 标记下线2.3 哨兵领袖2.4 新的主节点2.5 通知更新 3. …

JMeter案例分享:通过数据验证的错误,说说CSV数据文件设置中的线程共享模式

前言 用过JMeter参数化的小伙伴&#xff0c;想必对CSV Data Set Config非常熟悉。大家平时更关注变量名称&#xff0c;是否忽略首行等参数&#xff0c;其余的一般都使用默认值。然而我最近遇到一个未按照我的预想读取数据的案例&#xff0c;原因就出在最后一个参数“线程共享模…

服务重启时容器未自动启动

1、容器重启策略 通过设置容器的重启策略&#xff0c;‌可以决定在容器退出时Docker守护进程是否重启该容器。‌常见的重启策略包括&#xff1a;‌ no&#xff1a;‌不重启容器&#xff0c;‌默认策略。‌always&#xff1a;‌无论容器是如何退出的&#xff0c;‌总是重启容器…

keil配置irom偏移地址进行IAP,偏移地址不生效问题解决

如果keil配置了 IROM1 偏移地址&#xff0c;但是生成的hex&#xff0c;程序并没有偏移&#xff0c;问题多半是出现在linker里如下图所示。选择了分散加载&#xff0c;所以keil配置地址偏移不生效。 点开edit 更改分散加载的地址偏移即可。 偏移成功&#xff0c;可以IAP了。

算法学习笔记(8.5)-零钱兑换问题二

目录 Question&#xff1a; 动态规划思路&#xff1a; 代码实现&#xff1a; 空间优化代码 Question&#xff1a; 给定n种硬币&#xff0c;第i种硬币的面值为coins[i-1],目标金额为amt&#xff0c;每种硬币可以重复选取&#xff0c;问凑出目标金额的硬币组合数量。 动态规划思路…

Java 线程池详解

序言 在高并发编程中&#xff0c;线程池是一个非常重要的组件。它不仅能够有效地管理和复用线程资源&#xff0c;还可以提升应用程序的性能和稳定性。本文将详细介绍Java中的线程池机制&#xff0c;以及如何正确地使用线程池。 一、什么是线程池 线程池是一组已经初始化并等…

ftp pool 功能分析及 golang 实现

本文探究一种轻量级的 pool 实现 ftp 连接。 一、背景 简要介绍&#xff1a;业务中一般使用较多的是各种开源组件&#xff0c;设计有点重&#xff0c;因此本文探究一种轻量级的 pool 池的思想实现。 期望&#xff1a;设置连接池最大连接数为 N 时&#xff0c;批量执行 M 个 F…

超时导致SparkContext构造失败的问题探究

文章目录 1.前言2. 基于事故现场对问题进行分析2.1 日志分析2.2 单独测试Topology代码试图重现问题 3. 源码解析3.1 Client模式和Cluster模式下客户端的提交和启动过程客户端提交时在两种模式下的处理逻辑ApplicationMaster启动时在两种模式下的处理逻辑 3.2 两种模式下的下层角…

OSPF.综合实验

1、首先将各个网段基于172.16.0.0 16 进行划分 1.1、划分为4个大区域 172.16.0.0 18 172.16.64.0 18 172.16.128.0 18 172.16.192.0 18 四个网段 划分R4 划分area2 划分area3 划分area1 2、进行IP配置 如图使用配置指令进行配置 ip address x.x.x.x /x 并且将缺省路由…

uniapp编译成h5后接口请求参数变成[object object]

问题&#xff1a;uniapp编译成h5后接口请求参数变成[object object] 但是运行在开发者工具上没有一点问题 排查&#xff1a; 1&#xff1a;请求参数&#xff1a;看是否是在请求前就已经变成了[object object]了 结果&#xff1a; 一切正常 2&#xff1a;请求头&#xff1a;看…

2024辽宁省数学建模C题【改性生物碳对水中洛克沙胂和砷离子的吸附】原创论文分享

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024 年辽宁省大学数学建模竞赛C题改性生物碳对水中洛克沙胂和砷离子的吸附完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃…

OpenGL笔记九之彩色三角形与重心插值算法

OpenGL笔记九之彩色三角形与重心插值算法 —— 2024-07-07 晚上 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记九之彩色三角形与重心插值算法1.运行3.main.cpp 1.运行 3.main.cpp 代码 #include <iostream>#define DEBUG//注意&#xff1a;glad…

虚拟机centos连接xshell

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

VsCode远程ssh连接失败:Could not establish connection to XXX

一、问题描述 在VsCode中按下"F1"&#xff0c;选择Remote-SSH:Connect to Host 选择一个已经配置好的SSH主机&#xff0c;比如我选择的是192.168.0.104&#xff1a; 结果提示&#xff1a;Could not establish connection to XXX 二、解决方法 观察VsCode的输出信息…

几何建模-Parasolid中GO功能使用

1.背景介绍 1.1 Parasolid和它的接口间关系 1.2 什么是GO GO全称是Graphical Output.你的程序需要在屏幕或者打印设备上显示模型数据时。在需要使用PK中的某个渲染函数时创建图形显示数据时&#xff0c;Parasolid会调用GO相关的函数。GO函数会输出绘图指令给你的应用程序提供…

《昇思25天学习打卡营第20天|onereal》

应用实践/LLM原理和实践/基于MindSpore的GPT2文本摘要 基于MindSpore的GPT2文本摘要 数据集加载与处理 数据集加载 本次实验使用的是nlpcc2017摘要数据&#xff0c;内容为新闻正文及其摘要&#xff0c;总计50000个样本。 数据预处理 原始数据格式&#xff1a; article: [CLS…

MySQL-基础点

目录 MySQL概念 数据库三大范式是什么&#xff1f; blob 和 text 有什么区别&#xff1f; DATETIME 和 TIMESTAMP 的异同&#xff1f; MySQL 中 in 和 exists 的区别&#xff1f; MySQL 里记录货币用什么字段类型比较好&#xff1f; MySQL 怎么存储 emoji? 用过哪些 M…

MongoDB7出现:Windows下使用mongo命令提示不是内部或外部命令

确保环境变量添加正确的情况&#xff0c;仍然出现这种问题。如果安装的是新版本&#xff0c;则大概率是新版本mongodb的bin里面没有mongo命令 解决方案&#xff1a; 下载mongodb shell 下载链接 把shell的命令放进来 启用命令&#xff1a;mongosh

浅谈数学模型在UGC/AIGC游戏数值调参中的应用(AI智能体)

浅谈数学模型在UGC/AIGC游戏数值调参中的应用 ygluu 卢益贵 关键词&#xff1a;UGC、AIGC、AI智能体、大模型、数学模型、游戏数值调参、游戏策划 一、前言 在策划大大群提出《游戏工厂&#xff1a;AI&#xff08;AIGC/ChatGPT&#xff09;与流程式游戏开发》讨论之后就已完…