快速排序算法是一种分治排序算法,它将数组划分为两部分,然后分别对这两部分进行排序。它将重排序数组,使之满足一下三个条件:
1,对于某个i,a[i]在最终的位置上
2,a[l]…a[i-1]中的元素都比a[i]小
3,a[i+1] …a[r]中的元素都比a[i]大
通过划分后完成本轮排序,然后递归地处理子文件。
如果数组中有一个或者0个元素,就什么都不做。否则,调用以下算法实现快速排序:
int partition(Itema[],int l,int r)
{
int i=l-1,j=r;
Item v=a[r];
for (;;)
{
while (less(a[++i],v))
{
;
}
while (less(v,a[--j]))
{
if (j==1)
{
break;
}
}
if (i>=j)
{
break;
}
exch(a[i],a[j]);
}
exch(a[i],a[r]);
return i;
}
变量v保存了划分元素a[r],i和j分别是左扫描指针和右扫描指针。划分循环使得i增加j减小,while循环保持一个不变的性质---------i左侧没有元素比v大,j右侧没有元素比v小。一旦两个指针相遇,我们就交换啊a[i]和a[r],,这样v左侧的元素都小于v,v右侧的元素都大于等于v,结束划分过程。
划分是一个不确定的过程,当两个指针相遇,就通过break语句结束。测试j==1用来防止划分元素是文件总的最小元素。
voidquichsort(Item a[],int l,int r)
{
int i;
if (r<=l)
{
return;
}
i=partition(a,l,r);
quichsort(a,l,i-1);
quichsort(a,i+1,r);
}
注:快速排序是一个递归划分过程,我们对一个文件进行划分,划分原则是将一些元素放在它最终的位置上,对数组进行排序使比划分元素小的元素都在划分元素的左边,比划分元素大的元素都在划分元素的右边,然后分别对左右两部分进行递归处理。然后,从数组的左端进行扫描,直到找到一个大于划分元素的元素,同时从数据的右端开始扫描,直到找到一个小于划分元素的元素。使扫描停止的这个两个元素,显然在最终的划分数组中的位置相反,于是交换二者。继续这一过程,我们就可以保证比划分元素小的元素都在划分元素的左边,比划分元素大的元素都在划分元素的右边。
性能特征:
快速排序最坏情况下使用N*N/2次比较
快速排序平均情况下使用2*N*㏒N次比较
快速排序算法是一种不稳定的算法。
算法示意图
A
S
0
R
T
I
N
G
E
X
A
M
P
L
E
A
A
E
E
O
S
R
A
A
E
最近在做一个互联网项目,不可避免和其他互联网项目一样,需要各种类似的功能。其中一个就是通过用户访问网站的IP地址显示当前用户所在地。在没有查阅资料之前,我第一个想到的是应该有这样的RESTFUL服务,可能会有免费的,也有可能会是收费的。后来查阅了相关资料和咨询了客服人员发现了一款相当不错的库:GEOIP。
中文官网:http://www.maxmind.com/zh/home?pkit_lang=zh
英文官网:http://www.maxmind.com/en/home
而且GeoIP也提供了如我之前猜测的功能:Web Services,具体可以看这里:http://dev.maxmind.com/geoip/legacy/web-services
这里我就把官方的实例搬过来吧,Web Services :
首先来个Java 版本:
import java.net.MalformedURLException; import java.net.URL; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class OmniReader { public static void main(String[] args) throws Exception { String license_key = "YOUR_LICENSE_KEY"; String ip_address = "24.24.24.24"; String url_str = "http://geoip.maxmind.com/e?l=" + license_key + "&i=" + ip_address; URL url = new URL(/blog_article/url_str/index.html); BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream())); String inLine; while ((inLine = in.readLine()) != null) { // Alternatively use a CSV parser here. Pattern p = Pattern.compile("\"([^\"]*)\"|(?<=,|^)([^,]*)(?:,|$)"); Matcher m = p.matcher(inLine); ArrayList fields = new ArrayList(); String f; while (m.find()) { f = m.group(1); if (f!=null) { fields.add(f); } else { fields.add(m.group(2)); } } String countrycode = fields.get(0); String countryname = fields.get(1); String regioncode = fields.get(2); String regionname = fields.get(3); String city = fields.get(4); String lat = fields.get(5); String lon = fields.get(6); String metrocode = fields.get(7); String areacode = fields.get(8); String timezone = fields.get(9); String continent = fields.get(10); String postalcode = fields.get(11); String isp = fields.get(12); String org = fields.get(13); String domain = fields.get(14); String asnum = fields.get(15); String netspeed = fields.get(16); String usertype = fields.get(17); String accuracyradius = fields.get(18); String countryconf = fields.get(19); String cityconf = fields.get(20); String regionconf = fields.get(21); String postalconf = fields.get(22); String error = fields.get(23); } in.close(); } }
C#版本:
private string GetMaxMindOmniData(string IP) { System.Uri objUrl = new System.Uri("http://geoip.maxmind.com/e?l=YOUR_LICENSE_KEY&i=" + IP); System.Net.WebRequest objWebReq; System.Net.WebResponse objResp; System.IO.StreamReader sReader; string strReturn = string.Empty; try { objWebReq = System.Net.WebRequest.Create(objUrl); objResp = objWebReq.GetResponse(); sReader = new System.IO.StreamReader(objResp.GetResponseStream()); strReturn = sReader.ReadToEnd(); sReader.Close(); objResp.Close(); } catch (Exception ex) { } finally { objWebReq = null; } return strReturn; }
Ruby版本:
#!/usr/bin/env ruby require 'csv' require 'net/http' require 'open-uri' require 'optparse' require 'uri' fields = [:country_code, :country_name, :region_code, :region_name, :city_name, :latitude, :longitude, :metro_code, :area_code, :time_zone, :continent_code, :postal_code, :isp_name, :organization_name, :domain, :as_number, :netspeed, :user_type, :accuracy_radius, :country_confidence, :city_confidence, :region_confidence, :postal_confidence, :error] options = { :license => "YOUR_LICENSE_KEY", :ip => "24.24.24.24" } OptionParser.new { |opts| opts.banner = "Usage: omni-geoip-ws.rb [options]" opts.on("-l", "--license LICENSE", "MaxMind license key") do |l| options[:license] = l end opts.on("-i", "--ip IPADDRESS", "IP address to look up") do |i| options[:ip] = i end }.parse! uri = URI::HTTP.build(:scheme => 'http', :host => 'geoip.maxmind.com', :path => '/e', :query => URI.encode_www_form(:l => options[:license], :i => options[:ip])) response = Net::HTTP.get_response(uri) unless response.is_a?(Net::HTTPSuccess) abort "Request failed with status #{response.code}" end omni = Hash[fields.zip(response.body.encode('utf-8', 'iso-8859-1').parse_csv)] if omni[:error] abort "MaxMind returned an error code for the request: #{omni[:error]}" else puts puts "MaxMind Omni data for #{options[:ip]}"; puts omni.each { |key, val| printf " %-20s %s\n", key, val } puts end
最后再来个Perl版本吧:
#!/usr/bin/env perl use strict; use warnings; use Encode qw( decode ); use Getopt::Long; use LWP::UserAgent; use Text::CSV_XS; use URI; use URI::QueryParam; my @fields = qw( country_code country_name region_code region_name city_name latitude longitude metro_code area_code time_zone continent_code postal_code isp_name organization_name domain as_number netspeed user_type accuracy_radius country_confidence city_confidence region_confidence postal_confidence error ); my $license_key = 'YOUR_LICENSE_KEY'; my $ip_address = '24.24.24.24'; GetOptions( 'license:s' => \$license_key, 'ip:s' => \$ip_address, ); my $uri = URI->new('http://geoip.maxmind.com/e'); $uri->query_param( l => $license_key ); $uri->query_param( i => $ip_address ); my $ua = LWP::UserAgent->new( timeout => 5 ); my $response = $ua->get($uri); die 'Request failed with status ' . $response->code() unless $response->is_success(); my $csv = Text::CSV_XS->new( { binary => 1 } ); $csv->parse( decode( 'ISO-8859-1', $response->content() ) ); my %omni; @omni{@fields} = $csv->fields(); binmode STDOUT, ':encoding(UTF-8)'; if ( defined $omni{error} && length $omni{error} ) { die "MaxMind returned an error code for the request: $omni{error}\n"; } else { print "\nMaxMind Omni data for $ip_address\n\n"; for my $field (@fields) { print sprintf( " %-20s %s\n", $field, $omni{$field} ); } print "\n"; }
以上都是基于Web Services的,这里我再写个使用本地库的实例,而项目中也使用的是本地库。
首先在项目中需要添加库文件:GeoLiteCity,然后就可以利用这个文件得到城市和国家信息了,Java代码:
public class GeoBusiness { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub System.out.println(getLocationByIp("220.181.111.147").getCity()); System.out.println(getLocationByIp("220.181.111.147").getCountryName()); } public static Location getLocationByIp(String ipaddr) throws IOException { String sep = System.getProperty("file.separator"); String dir = Play.configuration.getProperty("geoip.datdir"); String dbfile = dir + sep + "GeoLiteCity.dat"; LookupService cl = new LookupService(dbfile, LookupService.GEOIP_MEMORY_CACHE); Location location = cl.getLocation(ipaddr); cl.close(); return location; } }
更详细的功能比如获取省,经纬度可以参考我最前面给出的网址。
优化系统的想法真不好简单地说的明白,这样吧,我爸在陕西西安,我妈在安徽的合肥,我弟弟在深圳,打算坐飞机到北京我这里玩。家人都比较节省,打算到了机场后互相等对方,然后一起坐车租车到我住的地方。然后让我给订机票,这可难住我了。
我查了qunar,一天从西安,合肥,深圳到北京有很多班飞机,怎么样让他们总的机票价格最少,并且在机场互相等待的时间降到最低,这里假设一个人等10分钟相当于1块钱,这样我们的目标就是schedulcost=(父亲机票钱+母亲hf-bj机票钱+弟弟机票钱+父亲等候分钟/10+母亲等候分钟/10+弟弟等候时间/10)最小,这里面有可能父亲,或者母亲或者弟弟的等候分钟是0。
先定义我的家人:
people = [('baba','XA'), ('mama','HF'), ('didi','sz')]爸爸从西安出发,妈妈从合肥出发,弟弟从深圳出发;
再定义航班信息:
flights = [('XA','BJ') : [('06:30', '08:30', 600), ('07:45', '10:15', 500)....], ('HF','BJ') : [('06:45', '09:30', 900), ('07:25', '10:45', 800)....], ('SZ','BJ') : [('06:25', '10:30', 1200), ('08:05', '12:10', 1300)....], ]
从西安到北京的航班有:6:30出发,8:30到达,机票600元;07:45出发,10:15到达,机票500元。。。
从合肥到北京的航班有:6:45出发,9:30到达,机票900元;07:25出发,10:45到达,机票800元。。。
从深圳到北京的航班有:6:25出发,10:30到达,机票1200元;08:05出发,12:10到达,机票1300元。。。
下面假设我定的航班是:
sol = [2,2,1]从西安到北京的第二班,从合肥到北京的第二班,从西安到北京的第一班
下面就可以算计我上面定的航班的花费了:其中
def schedulecost(sol): totalprice=0 latestarrival=0 for d in range(len(sol)): origin=people[d][1] outbound=flights[(origin,'BJ')][int(sol[d])] totalprice+=outbound[2] if latestarrival<getminutes(outbound[1]): latestarrival=getminutes(outbound[1]) totalwait=0 for d in range(len(sol)): origin=people[d][1] outbound=flights[(origin,'BJ')][int(sol[d])] totalwait+=latestarrival-getminutes(outbound[1]) return totalprice+totalwait/10很好懂,shedulecost计算了总的航班机票钱和总的等候时间,
计算总的等候时候先找出最晚到达的一班飞机的时间latestarrival。
我们的目标:找出某种组合的sol,让schedulecost最低;
因为一天内的航班有很多,并且我已经把问题简单到最低,所以在现实情况中很容易sol的各种组合数会上亿。
如果shedulecost稍微复杂的话,在各种sol中找到最优解是不现实的。下面看看几种可能的做法:
(一)随机搜索法
最简单的,随机计算其中的一部分结果,比如100条可能的解,最终的结果取这部分计算结果中的最小值,这种算法最简单,问题也最明显。
下面的实现,第一个参数是每种可能结果的范围,比如从西安到北京一天有100个航班,从合肥到北京一天有80个航班,从深圳到北京一天有150个航班,那么domain的定义就是domain=[(0,100),(0,80),(0,150)];第二个参数就是上面定义的schedulecost函数;
def randomoptimize(domain,costf): best=999999999 bestr=None for i in range(0,1000): # Create a random solution r=[float(random.randint(domain[i][0],domain[i][1])) for i in range(len(domain))] # Get the cost cost=costf(r) # Compare it to the best one so far if cost<best: best=cost bestr=r return r一千次的尝试可能是总解总很说得好的一部分,但是在多试几次之后,也许会得到一个差不多的解;
随机搜索法的问题在于,随机搜索是跳跃性的,这次的计算和上次的计算之前没有任何关系,也就是最新一次计算并没能利用之前已发现的优解,下面看看几种改进的算法:
(二)模拟爬山法
爬山法从一个随机解开始,然后在其临近的解中寻找更好的题解。在本例中,亦即找到所有相对于最初的随机安排,能够让某个人乘坐的航班稍早或者稍晚一些的安排,沃恩对每一个相邻的时间安排都进行成本计算,具有最低成本的安排将成为新的题解。重复这一过程知道没有相邻安排能够改善成本为止:
def hillclimb(domain,costf): # Create a random solution sol=[random.randint(domain[i][0],domain[i][1]) for i in range(len(domain))] # Main loop while 1: # Create list of neighboring solutions neighbors=[] for j in range(len(domain)): # One away in each direction if sol[j]>domain[j][0]: neighbors.append(sol[0:j]+[sol[j]+1]+sol[j+1:]) if sol[j]<domain[j][1]: neighbors.append(sol[0:j]+[sol[j]-1]+sol[j+1:]) # See what the best solution amongst the neighbors is current=costf(sol) best=current for j in range(len(neighbors)): cost=costf(neighbors[j]) if cost<best: best=cost sol=neighbors[j] # If there's no improvement, then we've reached the top if best==current: break return sol
之所以叫爬山法,就是我们沿着一条下坡路一直走到坡底,这个方法的速度很快,并且找到的结果一般会比随机搜索法好一些。但是也有个问题,这种算法的假设是最优解就在初始下坡位置的波谷处,第一次的随机解会影响最终的效果。所以这种算法可以求出局部最优解,而不是全局最优解。
(三)模拟退火法
退火指合金在加热后再慢慢冷却的过程。大量的原子因为受到激发而向周围跳跃,然后又逐渐稳定到一个低能阶的状态。
有时候在找到最优解前转向一个更差的解是有必要的。退火法一个随机解开始,用一个变量表示温度,温度在最开始会很高,尔后慢慢变低。每一次迭代周期,算法会随机选中题解中的某个数字,然后朝某个方向变化。这个算法和爬山法的区别是。爬山法每次的迭代都会朝成本最低的发展,而退火法不一定,有一定的比